diff --git a/.gitignore b/.gitignore
index 4eed0f0e47..0461ead471 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
+/.cache
/.vs
+/.vscode
/build*
CMakeSettings.json
docs/aidocs/*
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 5b97de5cd2..f46929d6fa 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -1,4 +1,5 @@
add_subdirectory(core)
+add_subdirectory(social)
add_files(
network.cpp
diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp
index 85a20ed30d..abd0470e76 100644
--- a/src/network/network_client.cpp
+++ b/src/network/network_client.cpp
@@ -29,6 +29,7 @@
#include "network_base.h"
#include "network_client.h"
#include "network_gamelist.h"
+#include "social/loader.h"
#include "../core/backup_type.hpp"
#include "../thread.h"
@@ -845,6 +846,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
SetLocalCompany(_network_join.company);
}
+ JoinData data = {
+ "Unknown", // TODO: fix this
+ this->connection_string.c_str()
+ };
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SERVER_JOINED, &data);
+
return NETWORK_RECV_STATUS_OKAY;
}
diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp
index 3aac31d00c..2c7d9353fb 100644
--- a/src/network/network_coordinator.cpp
+++ b/src/network/network_coordinator.cpp
@@ -15,6 +15,7 @@
#include "../strings_func.h"
#include "../window_func.h"
#include "../window_type.h"
+#include "social/loader.h"
#include "network.h"
#include "network_coordinator.h"
#include "network_gamelist.h"
@@ -194,6 +195,13 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
* attempt to re-use when registering again. */
_network_server_invite_code = _settings_client.network.server_invite_code;
+ JoinData data = {
+ _settings_client.network.server_name.c_str(),
+ _network_server_invite_code.c_str()
+ };
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SERVER_JOINED, &data);
+
SetWindowDirty(WC_CLIENT_LIST, 0);
if (_network_dedicated) {
diff --git a/src/network/social/CMakeLists.txt b/src/network/social/CMakeLists.txt
new file mode 100644
index 0000000000..6334ebcf27
--- /dev/null
+++ b/src/network/social/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_files(
+ loader.cpp
+ loader.h
+ social_api.h
+)
diff --git a/src/network/social/loader.cpp b/src/network/social/loader.cpp
new file mode 100644
index 0000000000..c4dea7126f
--- /dev/null
+++ b/src/network/social/loader.cpp
@@ -0,0 +1,52 @@
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file network_chat_gui.cpp GUI for handling chat messages. */
+
+#include "../../stdafx.h"
+#include "../network_func.h"
+#include "loader.h"
+
+void callback(const char* serverName) {
+ NetworkClientConnectGame(serverName, COMPANY_SPECTATOR);
+}
+
+SocialPlatformLoader::SocialPlatformLoader() {
+ LoadSocialPlatforms(this->plugins);
+
+ for (SocialPlatformPlugin plugin : plugins) {
+ plugin.initialize(callback, &plugin.userdata);
+ }
+}
+
+void SocialPlatformLoader::Shutdown() {
+ for (SocialPlatformPlugin plugin : plugins) {
+ plugin.shutdown(plugin.userdata);
+ }
+}
+
+void SocialPlatformLoader::RunDispatch() {
+ for (SocialPlatformPlugin plugin : plugins) {
+ plugin.dispatch(plugin.userdata);
+ }
+}
+
+void SocialPlatformLoader::NewState(eventCode event, void* parameter) {
+ for (SocialPlatformPlugin plugin : plugins) {
+ plugin.newState(event, parameter, plugin.userdata);
+ }
+}
+
+SocialPlatformLoader* SocialPlatformLoader::GetInstance() {
+ static SocialPlatformLoader* loader = nullptr;
+
+ if (loader == nullptr) {
+ loader = new SocialPlatformLoader();
+ }
+
+ return loader;
+}
diff --git a/src/network/social/loader.h b/src/network/social/loader.h
new file mode 100644
index 0000000000..2eb6121513
--- /dev/null
+++ b/src/network/social/loader.h
@@ -0,0 +1,44 @@
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file network_client.h Client part of the network protocol. */
+
+#ifndef NETWORK_SOCIAL_LOADER_H
+#define NETWORK_SOCIAL_LOADER_H
+
+#include "../../core/alloc_type.hpp"
+#include "social_api.h"
+
+struct SocialPlatformPlugin {
+ void* handle;
+
+ OTTD_Social_Initialize initialize;
+ OTTD_Social_Shutdown shutdown;
+ OTTD_Social_Dispatch dispatch;
+ OTTD_Social_NewState newState;
+
+ void* userdata;
+};
+
+class SocialPlatformLoader : public ZeroedMemoryAllocator {
+public:
+ void Shutdown();
+ void RunDispatch();
+ void NewState(eventCode event, void* parameter);
+
+ static SocialPlatformLoader* GetInstance();
+
+private:
+ SocialPlatformLoader();
+
+ std::vector plugins;
+};
+
+/* Defined in os//social_.cpp. */
+void LoadSocialPlatforms(std::vector& plugins);
+
+#endif
diff --git a/src/network/social/social_api.h b/src/network/social/social_api.h
new file mode 100644
index 0000000000..714e2b27a0
--- /dev/null
+++ b/src/network/social/social_api.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file social_api.h Defines the plug-in interface for social platforms. */
+
+#ifndef NETWORK_SOCIAL_API_H
+#define NETWORK_SOCIAL_API_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct JoinData {
+ // Name of the server as shown to players.
+ const char* server_name;
+
+ // String representation of the invite code or IP address.
+ const char* connection_string;
+};
+
+enum eventCode {
+ // Called when the player has entered the main menu.
+ // Parameter: N/A
+ OTTD_SOCIAL_EVENT_MENU,
+
+ // Called when the player loads a map in single player mode.
+ // Parameter: N/A
+ OTTD_SOCIAL_EVENT_SINGLE_PLAYER,
+
+ // Called during server join.
+ // Parameter: Relevant struct (OTTD_Social_Event_Server_Joined_Data*)
+ OTTD_SOCIAL_EVENT_SERVER_JOINED,
+
+ // Called during company allegiance changes.
+ // Parameter Company name (const char *)
+ // NULL if the player is just spectating.
+ OTTD_SOCIAL_EVENT_COMPANY_CHANGED,
+};
+
+// Callback provided by OpenTTD for the implementation to allow joining a game.
+typedef void (* OTTD_Social_JoinCallback)(const char* serverName);
+
+// Initializes the plugin.
+// The plugin is free to initialize the memory pointed to at the given address with any structure it needs to keep data around.
+// The plugin loader will keep track of this memory for the plugin.
+typedef bool (* OTTD_Social_Initialize)(OTTD_Social_JoinCallback callback, void **userdata);
+
+// Called by the plugin loader to indicate that OpenTTD is currently shutting down.
+// The plugin is responsible for freeing its user data, if it provided or used any.
+typedef void (* OTTD_Social_Shutdown)(void *userdata);
+
+// Called during the game loop to allow any plugin to pump its messages, if needed.
+typedef void (* OTTD_Social_Dispatch)(void *userdata);
+
+// Called when the game's state changes.
+typedef void (* OTTD_Social_NewState)(eventCode event, void* parameter, void *userdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 8b05cfe9af..a82850c8a3 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -35,6 +35,7 @@
#include "screenshot.h"
#include "network/network.h"
#include "network/network_func.h"
+#include "network/social/loader.h"
#include "ai/ai.hpp"
#include "ai/ai_config.hpp"
#include "settings_func.h"
@@ -1040,6 +1041,8 @@ void SwitchToMode(SwitchMode new_mode)
case SM_EDITOR: // Switch to scenario editor
MakeNewEditorWorld();
GenerateSavegameId();
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
break;
case SM_RELOADGAME: // Reload with what-ever started the game
@@ -1063,6 +1066,8 @@ void SwitchToMode(SwitchMode new_mode)
case SM_NEWGAME: // New Game --> 'Random game'
MakeNewGame(false, new_mode == SM_NEWGAME);
GenerateSavegameId();
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
break;
case SM_LOAD_GAME: { // Load game, Play Scenario
@@ -1079,6 +1084,8 @@ void SwitchToMode(SwitchMode new_mode)
OnStartGame(_network_dedicated);
/* Decrease pause counter (was increased from opening load dialog) */
Command::Post(PM_PAUSED_SAVELOAD, false);
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
}
break;
}
@@ -1087,6 +1094,8 @@ void SwitchToMode(SwitchMode new_mode)
case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
MakeNewGame(true, new_mode == SM_START_HEIGHTMAP);
GenerateSavegameId();
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
break;
case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
@@ -1095,6 +1104,8 @@ void SwitchToMode(SwitchMode new_mode)
GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
GenerateSavegameId();
MarkWholeScreenDirty();
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
break;
case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
@@ -1108,6 +1119,8 @@ void SwitchToMode(SwitchMode new_mode)
SetDParamStr(0, GetSaveLoadErrorString());
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_CRITICAL);
}
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_SINGLE_PLAYER, nullptr);
break;
}
@@ -1130,6 +1143,8 @@ void SwitchToMode(SwitchMode new_mode)
ShowNetworkAskSurvey();
}
}
+
+ SocialPlatformLoader::GetInstance()->NewState(OTTD_SOCIAL_EVENT_MENU, nullptr);
break;
case SM_SAVE_GAME: // Save game.
@@ -1536,6 +1551,7 @@ void GameLoop()
if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();
+ SocialPlatformLoader::GetInstance()->RunDispatch();
SoundDriver::GetInstance()->MainLoop();
MusicLoop();
}
diff --git a/src/os/windows/CMakeLists.txt b/src/os/windows/CMakeLists.txt
index 9215514fa2..030cb07d91 100644
--- a/src/os/windows/CMakeLists.txt
+++ b/src/os/windows/CMakeLists.txt
@@ -4,6 +4,7 @@ add_files(
font_win32.h
string_uniscribe.cpp
string_uniscribe.h
+ social_win.cpp
survey_win.cpp
win32.cpp
win32.h
diff --git a/src/os/windows/social_win.cpp b/src/os/windows/social_win.cpp
new file mode 100644
index 0000000000..26b6cce29f
--- /dev/null
+++ b/src/os/windows/social_win.cpp
@@ -0,0 +1,53 @@
+/*
+ * This file is part of OpenTTD.
+ * 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.
+ * 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.
+ * 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 .
+ */
+
+/** @file survey_win.cpp Windows implementation of OS-specific survey information. */
+
+#include "../../stdafx.h"
+
+#include "../../network/social/loader.h"
+#include "../../fileio_func.h"
+
+#include
+#include
+
+#include "../../safeguards.h"
+
+void LoadSocialPlatforms(std::vector& plugins) {
+ std::string search_dir = FioGetDirectory(SP_BINARY_DIR, BASE_DIR);
+
+ WIN32_FIND_DATAW find_data = {};
+ HANDLE find_handle = FindFirstFileW(OTTD2FS(search_dir + "*.ots").c_str(), &find_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ do {
+ std::string library_path = search_dir + FS2OTTD(find_data.cFileName);
+ HMODULE library = LoadLibraryW(OTTD2FS(library_path).c_str());
+ if (library == nullptr) {
+ continue;
+ }
+
+ SocialPlatformPlugin plugin = {
+ library,
+
+ (OTTD_Social_Initialize)GetProcAddress(library, "OTTD_Social_Initialize"),
+ (OTTD_Social_Shutdown)GetProcAddress(library, "OTTD_Social_Shutdown"),
+ (OTTD_Social_Dispatch)GetProcAddress(library, "OTTD_Social_Dispatch"),
+ (OTTD_Social_NewState)GetProcAddress(library, "OTTD_Social_NewState"),
+
+ nullptr
+ };
+
+ if (plugin.initialize == nullptr || plugin.shutdown == nullptr || plugin.dispatch == nullptr || plugin.newState == nullptr) {
+ FreeLibrary(library);
+ }
+
+ plugins.push_back(plugin);
+ } while (FindNextFileW(find_handle, &find_data));
+
+ FindClose(find_handle);
+ }
+}