OpenTTD Source  20241108-master-g80f628063a
social_integration.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 
12 #include "social_integration.h"
13 #include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
14 
15 #include "debug.h"
16 #include "fileio_func.h"
17 #include "library_loader.h"
18 #include "rev.h"
19 #include "string_func.h"
20 #include "signature.h"
21 
22 #include "safeguards.h"
23 
28 public:
29  InternalSocialIntegrationPlugin(const std::string &filename, const std::string &basepath) : library(nullptr), external(basepath)
30  {
31  openttd_info.openttd_version = _openttd_revision;
32 
33  if (!ValidateSignatureFile(fmt::format("{}.sig", filename))) {
35  return;
36  }
37 
38  this->library = std::make_unique<LibraryLoader>(filename);
39  }
40 
41  OpenTTD_SocialIntegration_v1_PluginInfo plugin_info = {};
42  OpenTTD_SocialIntegration_v1_PluginApi plugin_api = {};
43  OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info = {};
44 
45  std::unique_ptr<LibraryLoader> library = nullptr;
46 
48 };
49 
50 static std::vector<std::unique_ptr<InternalSocialIntegrationPlugin>> _plugins;
51 static std::set<std::string> _loaded_social_platform;
52 
55 public:
56  void Scan()
57  {
58 #ifdef _WIN32
59  std::string extension = "-social.dll";
60 #elif defined(__APPLE__)
61  std::string extension = "-social.dylib";
62 #else
63  std::string extension = "-social.so";
64 #endif
65 
66  this->FileScanner::Scan(extension, SOCIAL_INTEGRATION_DIR, false);
67  }
68 
69  bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
70  {
71  std::string basepath = filename.substr(basepath_length);
72  Debug(misc, 1, "[Social Integration: {}] Loading ...", basepath);
73 
74  auto &plugin = _plugins.emplace_back(std::make_unique<InternalSocialIntegrationPlugin>(filename, basepath));
75 
76  /* Validation failed, so no library was loaded. */
77  if (plugin->library == nullptr) {
78  return false;
79  }
80 
81  if (plugin->library->HasError()) {
82  plugin->external.state = SocialIntegrationPlugin::FAILED;
83 
84  Debug(misc, 0, "[Social Integration: {}] Failed to load library: {}", basepath, plugin->library->GetLastError());
85  return false;
86  }
87 
88  OpenTTD_SocialIntegration_v1_GetInfo getinfo_func = plugin->library->GetFunction("SocialIntegration_v1_GetInfo");
89  if (plugin->library->HasError()) {
90  plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
91 
92  Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_GetInfo: {}", basepath, plugin->library->GetLastError());
93  return false;
94  }
95 
96  OpenTTD_SocialIntegration_v1_Init init_func = plugin->library->GetFunction("SocialIntegration_v1_Init");
97  if (plugin->library->HasError()) {
98  plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
99 
100  Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_Init: {}", basepath, plugin->library->GetLastError());
101  return false;
102  }
103 
104  getinfo_func(&plugin->plugin_info);
105  /* Setup the information for the outside world to see. */
106  plugin->external.social_platform = plugin->plugin_info.social_platform;
107  plugin->external.name = plugin->plugin_info.name;
108  plugin->external.version = plugin->plugin_info.version;
109 
110  /* Lowercase the string for comparison. */
111  std::string lc_social_platform = plugin->plugin_info.social_platform;
112  strtolower(lc_social_platform);
113 
114  /* Prevent more than one plugin for a certain Social Platform to be loaded, as that never ends well. */
115  if (_loaded_social_platform.find(lc_social_platform) != _loaded_social_platform.end()) {
116  plugin->external.state = SocialIntegrationPlugin::DUPLICATE;
117 
118  Debug(misc, 0, "[Social Integration: {}] Another plugin for {} is already loaded", basepath, plugin->plugin_info.social_platform);
119  return false;
120  }
121  _loaded_social_platform.insert(lc_social_platform);
122 
123  auto state = init_func(&plugin->plugin_api, &plugin->openttd_info);
124  switch (state) {
125  case OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS:
126  plugin->external.state = SocialIntegrationPlugin::RUNNING;
127 
128  Debug(misc, 1, "[Social Integration: {}] Loaded for {}: {} ({})", basepath, plugin->plugin_info.social_platform, plugin->plugin_info.name, plugin->plugin_info.version);
129  return true;
130 
131  case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED:
132  plugin->external.state = SocialIntegrationPlugin::FAILED;
133 
134  Debug(misc, 0, "[Social Integration: {}] Failed to initialize", basepath);
135  return false;
136 
137  case OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING:
138  plugin->external.state = SocialIntegrationPlugin::PLATFORM_NOT_RUNNING;
139 
140  Debug(misc, 1, "[Social Integration: {}] Failed to initialize: {} is not running", basepath, plugin->plugin_info.social_platform);
141  return false;
142 
143  default:
144  NOT_REACHED();
145  }
146  }
147 };
148 
149 std::vector<SocialIntegrationPlugin *> SocialIntegration::GetPlugins()
150 {
151  std::vector<SocialIntegrationPlugin *> plugins;
152 
153  for (auto &plugin : _plugins) {
154  plugins.push_back(&plugin->external);
155  }
156 
157  return plugins;
158 }
159 
161 {
163  fs.Scan();
164 }
165 
172 template <typename T, typename... Ts>
173 static void PluginCall(std::unique_ptr<InternalSocialIntegrationPlugin> &plugin, T func, Ts... args)
174 {
175  if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
176  return;
177  }
178 
179  if (func != nullptr) {
180  func(args...);
181  }
182 }
183 
185 {
186  for (auto &plugin : _plugins) {
187  PluginCall(plugin, plugin->plugin_api.shutdown);
188  }
189 
190  _plugins.clear();
191  _loaded_social_platform.clear();
192 }
193 
195 {
196  for (auto &plugin : _plugins) {
197  if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
198  continue;
199  }
200 
201  if (plugin->plugin_api.run_callbacks != nullptr) {
202  if (!plugin->plugin_api.run_callbacks()) {
203  Debug(misc, 1, "[Social Plugin: {}] Requested to be unloaded", plugin->external.basepath);
204 
205  _loaded_social_platform.erase(plugin->plugin_info.social_platform);
206  plugin->external.state = SocialIntegrationPlugin::UNLOADED;
207  PluginCall(plugin, plugin->plugin_api.shutdown);
208  }
209  }
210  }
211 }
212 
214 {
215  for (auto &plugin : _plugins) {
216  PluginCall(plugin, plugin->plugin_api.event_enter_main_menu);
217  }
218 }
219 
220 void SocialIntegration::EventEnterScenarioEditor(uint map_width, uint map_height)
221 {
222  for (auto &plugin : _plugins) {
223  PluginCall(plugin, plugin->plugin_api.event_enter_scenario_editor, map_width, map_height);
224  }
225 }
226 
227 void SocialIntegration::EventEnterSingleplayer(uint map_width, uint map_height)
228 {
229  for (auto &plugin : _plugins) {
230  PluginCall(plugin, plugin->plugin_api.event_enter_singleplayer, map_width, map_height);
231  }
232 }
233 
234 void SocialIntegration::EventEnterMultiplayer(uint map_width, uint map_height)
235 {
236  for (auto &plugin : _plugins) {
237  PluginCall(plugin, plugin->plugin_api.event_enter_multiplayer, map_width, map_height);
238  }
239 }
240 
242 {
243  for (auto &plugin : _plugins) {
244  PluginCall(plugin, plugin->plugin_api.event_joining_multiplayer);
245  }
246 }
Helper for scanning for files with a given name.
Definition: fileio_func.h:37
uint Scan(std::string_view extension, Subdirectory sd, bool tars=true, bool recursive=true)
Scan for files with the given extension in the given search path.
Definition: fileio.cpp:1114
Container to track information per plugin.
std::unique_ptr< LibraryLoader > library
Library handle.
SocialIntegrationPlugin external
Information of the plugin to be used by other parts of our codebase.
OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info
Information supplied by OpenTTD.
OpenTTD_SocialIntegration_v1_PluginApi plugin_api
API supplied by plugin.
OpenTTD_SocialIntegration_v1_PluginInfo plugin_info
Information supplied by plugin.
Helper for scanning for files with SocialIntegration as extension.
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
Add a file with the given filename.
@ PLATFORM_NOT_RUNNING
The plugin failed to initialize because the Social Platform is not running.
@ UNSUPPORTED_API
The plugin does not support the current API version.
@ RUNNING
The plugin is successfully loaded and running.
@ FAILED
The plugin failed to initialize.
@ DUPLICATE
Another plugin of the same Social Platform is already loaded.
@ INVALID_SIGNATURE
The signature of the plugin is invalid.
@ UNLOADED
The plugin is unloaded upon request.
State state
Result of the plugin's init function.
static void EventEnterScenarioEditor(uint map_width, uint map_height)
Event: user entered the Scenario Editor.
static void EventEnterSingleplayer(uint map_width, uint map_height)
Event: user entered a singleplayer game.
static std::vector< SocialIntegrationPlugin * > GetPlugins()
Get the list of loaded social integration plugins.
static void RunCallbacks()
Allow any social integration library to handle their own events.
static void EventEnterMultiplayer(uint map_width, uint map_height)
Event: user entered a multiplayer game.
static void Shutdown()
Shutdown the social integration system, and all social integration plugins that are loaded.
static void EventEnterMainMenu()
Event: user entered the main menu.
static void EventJoiningMultiplayer()
Event: user is joining a multiplayer game.
static void Initialize()
Initialize the social integration system, loading any social integration plugins that are available.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
Functions for Standard In/Out file operations.
@ SOCIAL_INTEGRATION_DIR
Subdirectory for all social integration plugins.
Definition: fileio_type.h:131
Functions/types related to loading libraries dynamically.
declaration of OTTD revision dependent variables
A number of safeguards to prevent using unsafe methods.
bool ValidateSignatureFile(const std::string &filename)
Validate that the signatures mentioned in the signature file are matching the files in question.
Definition: signature.cpp:267
Routines to validate signature files.
static void PluginCall(std::unique_ptr< InternalSocialIntegrationPlugin > &plugin, T func, Ts... args)
Wrapper to call a function pointer of a plugin if it isn't a nullptr.
static std::vector< std::unique_ptr< InternalSocialIntegrationPlugin > > _plugins
List of loaded plugins.
static std::set< std::string > _loaded_social_platform
List of Social Platform plugins already loaded. Used to prevent loading a plugin for the same Social ...
Interface definitions for game to report/respond to social integration.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.