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); + } +}