Fix #10490: Allow ships to exit depots if another is not moving at the exit point...
[openttd-github.git] / src / social_integration.cpp
blob18e8e44eb772b0229d668fc1bd0ccb727d9b8beb
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 */
8 /** @file social_integration.cpp Base implementation of social integration support. */
10 #include "stdafx.h"
12 #include "social_integration.h"
13 #include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
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"
22 #include "safeguards.h"
24 /**
25 * Container to track information per plugin.
27 class InternalSocialIntegrationPlugin {
28 public:
29 InternalSocialIntegrationPlugin(const std::string &filename, const std::string &basepath) : library(nullptr), external(basepath)
31 openttd_info.openttd_version = _openttd_revision;
33 if (!ValidateSignatureFile(fmt::format("{}.sig", filename))) {
34 external.state = SocialIntegrationPlugin::INVALID_SIGNATURE;
35 return;
38 this->library = std::make_unique<LibraryLoader>(filename);
41 OpenTTD_SocialIntegration_v1_PluginInfo plugin_info = {}; ///< Information supplied by plugin.
42 OpenTTD_SocialIntegration_v1_PluginApi plugin_api = {}; ///< API supplied by plugin.
43 OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info = {}; ///< Information supplied by OpenTTD.
45 std::unique_ptr<LibraryLoader> library = nullptr; ///< Library handle.
47 SocialIntegrationPlugin external; ///< Information of the plugin to be used by other parts of our codebase.
50 static std::vector<std::unique_ptr<InternalSocialIntegrationPlugin>> _plugins; ///< List of loaded plugins.
51 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 Platform twice.
53 /** Helper for scanning for files with SocialIntegration as extension */
54 class SocialIntegrationFileScanner : FileScanner {
55 public:
56 void Scan()
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
66 this->FileScanner::Scan(extension.c_str(), SOCIAL_INTEGRATION_DIR, false);
69 bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
71 std::string basepath = filename.substr(basepath_length);
72 Debug(misc, 1, "[Social Integration: {}] Loading ...", basepath);
74 auto &plugin = _plugins.emplace_back(std::make_unique<InternalSocialIntegrationPlugin>(filename, basepath));
76 /* Validation failed, so no library was loaded. */
77 if (plugin->library == nullptr) {
78 return false;
81 if (plugin->library->HasError()) {
82 plugin->external.state = SocialIntegrationPlugin::FAILED;
84 Debug(misc, 0, "[Social Integration: {}] Failed to load library: {}", basepath, plugin->library->GetLastError());
85 return false;
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;
92 Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_GetInfo: {}", basepath, plugin->library->GetLastError());
93 return false;
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;
100 Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_Init: {}", basepath, plugin->library->GetLastError());
101 return false;
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;
110 /* Lowercase the string for comparison. */
111 std::string lc_social_platform = plugin->plugin_info.social_platform;
112 strtolower(lc_social_platform);
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;
118 Debug(misc, 0, "[Social Integration: {}] Another plugin for {} is already loaded", basepath, plugin->plugin_info.social_platform);
119 return false;
121 _loaded_social_platform.insert(lc_social_platform);
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;
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;
131 case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED:
132 plugin->external.state = SocialIntegrationPlugin::FAILED;
134 Debug(misc, 0, "[Social Integration: {}] Failed to initialize", basepath);
135 return false;
137 case OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING:
138 plugin->external.state = SocialIntegrationPlugin::PLATFORM_NOT_RUNNING;
140 Debug(misc, 1, "[Social Integration: {}] Failed to initialize: {} is not running", basepath, plugin->plugin_info.social_platform);
141 return false;
143 default:
144 NOT_REACHED();
149 std::vector<SocialIntegrationPlugin *> SocialIntegration::GetPlugins()
151 std::vector<SocialIntegrationPlugin *> plugins;
153 for (auto &plugin : _plugins) {
154 plugins.push_back(&plugin->external);
157 return plugins;
160 void SocialIntegration::Initialize()
162 SocialIntegrationFileScanner fs;
163 fs.Scan();
167 * Wrapper to call a function pointer of a plugin if it isn't a nullptr.
169 * @param plugin Plugin to call the function pointer on.
170 * @param func Function pointer to call.
172 template <typename T, typename... Ts>
173 static void PluginCall(std::unique_ptr<InternalSocialIntegrationPlugin> &plugin, T func, Ts... args)
175 if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
176 return;
179 if (func != nullptr) {
180 func(args...);
184 void SocialIntegration::Shutdown()
186 for (auto &plugin : _plugins) {
187 PluginCall(plugin, plugin->plugin_api.shutdown);
190 _plugins.clear();
191 _loaded_social_platform.clear();
194 void SocialIntegration::RunCallbacks()
196 for (auto &plugin : _plugins) {
197 if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
198 continue;
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);
205 _loaded_social_platform.erase(plugin->plugin_info.social_platform);
206 plugin->external.state = SocialIntegrationPlugin::UNLOADED;
207 PluginCall(plugin, plugin->plugin_api.shutdown);
213 void SocialIntegration::EventEnterMainMenu()
215 for (auto &plugin : _plugins) {
216 PluginCall(plugin, plugin->plugin_api.event_enter_main_menu);
220 void SocialIntegration::EventEnterScenarioEditor(uint map_width, uint map_height)
222 for (auto &plugin : _plugins) {
223 PluginCall(plugin, plugin->plugin_api.event_enter_scenario_editor, map_width, map_height);
227 void SocialIntegration::EventEnterSingleplayer(uint map_width, uint map_height)
229 for (auto &plugin : _plugins) {
230 PluginCall(plugin, plugin->plugin_api.event_enter_singleplayer, map_width, map_height);
234 void SocialIntegration::EventEnterMultiplayer(uint map_width, uint map_height)
236 for (auto &plugin : _plugins) {
237 PluginCall(plugin, plugin->plugin_api.event_enter_multiplayer, map_width, map_height);
241 void SocialIntegration::EventJoiningMultiplayer()
243 for (auto &plugin : _plugins) {
244 PluginCall(plugin, plugin->plugin_api.event_joining_multiplayer);