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/>.
8 /** @file social_integration.cpp Base implementation of social integration support. */
12 #include "social_integration.h"
13 #include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
16 #include "fileio_func.h"
17 #include "library_loader.h"
19 #include "string_func.h"
20 #include "signature.h"
22 #include "safeguards.h"
25 * Container to track information per plugin.
27 class InternalSocialIntegrationPlugin
{
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
;
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
{
59 std::string extension
= "-social.dll";
60 #elif defined(__APPLE__)
61 std::string extension
= "-social.dylib";
63 std::string extension
= "-social.so";
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) {
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());
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());
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());
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
);
121 _loaded_social_platform
.insert(lc_social_platform
);
123 auto state
= init_func(&plugin
->plugin_api
, &plugin
->openttd_info
);
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
);
131 case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED
:
132 plugin
->external
.state
= SocialIntegrationPlugin::FAILED
;
134 Debug(misc
, 0, "[Social Integration: {}] Failed to initialize", basepath
);
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
);
149 std::vector
<SocialIntegrationPlugin
*> SocialIntegration::GetPlugins()
151 std::vector
<SocialIntegrationPlugin
*> plugins
;
153 for (auto &plugin
: _plugins
) {
154 plugins
.push_back(&plugin
->external
);
160 void SocialIntegration::Initialize()
162 SocialIntegrationFileScanner fs
;
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
) {
179 if (func
!= nullptr) {
184 void SocialIntegration::Shutdown()
186 for (auto &plugin
: _plugins
) {
187 PluginCall(plugin
, plugin
->plugin_api
.shutdown
);
191 _loaded_social_platform
.clear();
194 void SocialIntegration::RunCallbacks()
196 for (auto &plugin
: _plugins
) {
197 if (plugin
->external
.state
!= SocialIntegrationPlugin::RUNNING
) {
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
);