Changeset - r28552:48eaa3286eee
README.md
Show inline comments
 
@@ -115,12 +115,21 @@ OpenTTD features multiple types of add-o
 

	
 
Most types of add-on content can be downloaded within OpenTTD via the 'Check Online Content' button in the main menu.
 

	
 
Add-on content can also be installed manually, but that's more complicated; the [OpenTTD wiki](https://wiki.openttd.org/) may offer help with that, or the [OpenTTD directory structure guide](./docs/directory_structure.md).
 

	
 

	
 
### 1.5.1) Social Integration
 

	
 
OpenTTD has the ability to load plugins to integrate with Social Platforms like Steam, Discord, etc.
 

	
 
To enable such integration, the plugin for the specific platform has to be downloaded and stored in the `social_integration` folder.
 

	
 
See [OpenTTD's website](https://www.openttd.org), under Downloads, for what plugins are available.
 

	
 

	
 
### 1.6) OpenTTD directories
 

	
 
OpenTTD uses its own directory structure to store game data, add-on content etc.
 

	
 
For more information, see the [directory structure guide](./docs/directory_structure.md).
 

	
 
@@ -195,11 +204,14 @@ The catch2 implementation in `src/3rdpar
 
See `src/3rdparty/catch2/LICENSE.txt` for the complete license text.
 

	
 
The icu scriptrun implementation in `src/3rdparty/icu` is licensed under the Unicode license.
 
See `src/3rdparty/icu/LICENSE` for the complete license text.
 

	
 
The monocypher implementation in `src/3rdparty/monocypher` is licensed under the 2-clause BSD and CC-0 license.
 
See src/3rdparty/monocypher/LICENSE.md` for the complete license text.
 
See `src/3rdparty/monocypher/LICENSE.md` for the complete license text.
 

	
 
The OpenTTD Social Integration API in `src/3rdparty/openttd_social_integration_api` is licensed under the MIT license.
 
See `src/3rdparty/openttd_social_integration_api/LICENSE` for the complete license text.
 

	
 
## 4.0 Credits
 

	
 
See [CREDITS.md](./CREDITS.md)
cmake/Options.cmake
Show inline comments
 
@@ -64,12 +64,13 @@ function(set_options)
 
    else()
 
        option(OPTION_USE_THREADS "Use threads" ON)
 
    endif()
 
    option(OPTION_USE_NSIS "Use NSIS to create windows installer; enable only for stable releases" OFF)
 
    option(OPTION_TOOLS_ONLY "Build only tools target" OFF)
 
    option(OPTION_DOCS_ONLY "Build only docs target" OFF)
 
    option(OPTION_ALLOW_INVALID_SIGNATURE "Allow loading of content with invalid signatures" OFF)
 

	
 
    if (OPTION_DOCS_ONLY)
 
        set(OPTION_TOOLS_ONLY ON PARENT_SCOPE)
 
    endif()
 

	
 
    option(OPTION_SURVEY_KEY "Survey-key to use for the opt-in survey (empty if you have none)" "")
 
@@ -89,12 +90,17 @@ function(show_options)
 

	
 
    if(OPTION_SURVEY_KEY)
 
        message(STATUS "Option Survey Key - USED")
 
    else()
 
        message(STATUS "Option Survey Key - NOT USED")
 
    endif()
 

	
 
    if(OPTION_ALLOW_INVALID_SIGNATURE)
 
        message(STATUS "Option Allow Invalid Signature - USED")
 
        message(WARNING "Ignoring invalid signatures is a security risk! Use with care!")
 
    endif()
 
endfunction()
 

	
 
# Add the definitions for the options that are selected.
 
#
 
# add_definitions_based_on_options()
 
#
 
@@ -113,7 +119,11 @@ function(add_definitions_based_on_option
 
        add_definitions(-DNDEBUG)
 
    endif()
 

	
 
    if(OPTION_SURVEY_KEY)
 
        add_definitions(-DSURVEY_KEY="${OPTION_SURVEY_KEY}")
 
    endif()
 

	
 
    if(OPTION_ALLOW_INVALID_SIGNATURE)
 
        add_definitions(-DALLOW_INVALID_SIGNATURE)
 
    endif()
 
endfunction()
src/3rdparty/CMakeLists.txt
Show inline comments
 
@@ -3,6 +3,7 @@ add_subdirectory(fmt)
 
add_subdirectory(icu)
 
add_subdirectory(md5)
 
add_subdirectory(monocypher)
 
add_subdirectory(squirrel)
 
add_subdirectory(nlohmann)
 
add_subdirectory(opengl)
 
add_subdirectory(openttd_social_integration_api)
src/3rdparty/openttd_social_integration_api/CMakeLists.txt
Show inline comments
 
new file 100644
 
add_files(
 
    openttd_social_integration_api.h
 
    openttd_social_integration_api_v1.h
 
)
src/3rdparty/openttd_social_integration_api/LICENSE
Show inline comments
 
new file 100644
 
 Copyright 2024 OpenTTD project
 

	
 
 Permission is hereby granted, free of charge, to any person obtaining
 
 a copy of this software and associated documentation files
 
 (the "Software"), to deal in the Software without restriction,
 
 including without limitation the rights to use, copy, modify, merge,
 
 publish, distribute, sublicense, and/or sell copies of the Software,
 
 and to permit persons to whom the Software is furnished to do so,
 
 subject to the following conditions:
 

	
 
 The above copyright notice and this permission notice shall be included
 
 in all copies or substantial portions of the Software.
 

	
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 
 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
 OTHER DEALINGS IN THE SOFTWARE.
src/3rdparty/openttd_social_integration_api/openttd_social_integration_api.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2024 OpenTTD project
 
 *
 
 * Permission is hereby granted, free of charge, to any person obtaining
 
 * a copy of this software and associated documentation files
 
 * (the "Software"), to deal in the Software without restriction,
 
 * including without limitation the rights to use, copy, modify, merge,
 
 * publish, distribute, sublicense, and/or sell copies of the Software,
 
 * and to permit persons to whom the Software is furnished to do so,
 
 * subject to the following conditions:
 
 *
 
 * The above copyright notice and this permission notice shall be included
 
 * in all copies or substantial portions of the Software.
 
 *
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
 * OTHER DEALINGS IN THE SOFTWARE.
 
 */
 

	
 
/*
 
 * Although all the source-files created by OpenTTD are licensed under the
 
 * GPL-v2, this file is an exception. This file is part of the API for
 
 * social integration plugins, and licensed under the MIT license, to allow
 
 * for non-free implementations.
 
 */
 

	
 
/** @file openttd_social_integration_api.h Interface definitions for plugins to report/respond to social integration. */
 

	
 
#ifndef OPENTTD_SOCIAL_INTEGRATION_API_H
 
#define OPENTTD_SOCIAL_INTEGRATION_API_H
 

	
 
#include "openttd_social_integration_api_v1.h"
 

	
 
#endif /* OPENTTD_SOCIAL_INTEGRATION_API_H */
src/3rdparty/openttd_social_integration_api/openttd_social_integration_api_v1.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2024 OpenTTD project
 
 *
 
 * Permission is hereby granted, free of charge, to any person obtaining
 
 * a copy of this software and associated documentation files
 
 * (the "Software"), to deal in the Software without restriction,
 
 * including without limitation the rights to use, copy, modify, merge,
 
 * publish, distribute, sublicense, and/or sell copies of the Software,
 
 * and to permit persons to whom the Software is furnished to do so,
 
 * subject to the following conditions:
 
 *
 
 * The above copyright notice and this permission notice shall be included
 
 * in all copies or substantial portions of the Software.
 
 *
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
 * OTHER DEALINGS IN THE SOFTWARE.
 
 */
 

	
 
/*
 
 * Although all the source-files created by OpenTTD are licensed under the
 
 * GPL-v2, this file is an exception. This file is part of the API for
 
 * social integration plugins, and licensed under the MIT license, to allow
 
 * for non-free implementations.
 
 */
 

	
 
/** @file v1.h Version 1 definition of the OpenTTD Social Integration Plugin API. */
 

	
 
#ifndef OPENTTD_SOCIAL_INTEGRATION_API_V1_H
 
#define OPENTTD_SOCIAL_INTEGRATION_API_V1_H
 

	
 
#ifdef __cplusplus
 
extern "C" {
 
#endif
 

	
 
/** Pointers supplied by the plugin for OpenTTD to use. */
 
struct OpenTTD_SocialIntegration_v1_PluginInfo {
 
	/**
 
	 * The Social Platform this plugin is for.
 
	 *
 
	 * UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
 
	 *
 
	 * As there can only be one plugin active for each Social Platform, this
 
	 * value is used to determine which plugin to use.
 
	 *
 
	 * A complete list of names can be found here:
 
	 * https://wiki.openttd.org/en/Development/Social%20Integration
 
	 *
 
	 * Please use names from that list, including capitalization.
 
	 *
 
	 * If you create a plugin for a new Social Platform, please add it to the
 
	 * wiki page.
 
	 */
 
	const char *social_platform;
 

	
 
	const char *name; ///< Full name of the plugin. UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
 
	const char *version; ///< Version of the plugin. UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
 
};
 

	
 
/** Pointers supplied by the plugin for OpenTTD to use. */
 
struct OpenTTD_SocialIntegration_v1_PluginApi {
 
	/**
 
	 * OpenTTD tells the plugin to shut down.
 
	 *
 
	 * The plugin should free any resources it allocated, and must not call any of the callback functions after this call.
 
	 */
 
	void (*shutdown)();
 

	
 
	/**
 
	 * OpenTTD calls this function at regular intervals, to handle any callbacks the plugin might have.
 
	 *
 
	 * It is also safe to call the OpenTTD_SocialIntegrationCallbacks functions here.
 
	 *
 
	 * @return True if the plugin wants to be called again, false if the plugin wants to be unloaded.
 
	 */
 
	bool (*run_callbacks)();
 

	
 
	/**
 
	 * The player has entered the main menu.
 
	 */
 
	void (*event_enter_main_menu)();
 

	
 
	/**
 
	 * The player has entered the Scenario Editor.
 
	 *
 
	 * @param map_width The width of the map in tiles.
 
	 * @param map_height The height of the map in tiles.
 
	 */
 
	void (*event_enter_scenario_editor)(unsigned int map_width, unsigned int map_height);
 

	
 
	/**
 
	 * The player has entered a singleplayer game.
 
	 *
 
	 * @param map_width The width of the map in tiles.
 
	 * @param map_height The height of the map in tiles.
 
	 */
 
	void (*event_enter_singleplayer)(unsigned int map_width, unsigned int map_height);
 

	
 
	/**
 
	 * The player has entered a multiplayer game.
 
	 *
 
	 * @param map_width The width of the map in tiles.
 
	 * @param map_height The height of the map in tiles.
 
	 */
 
	void (*event_enter_multiplayer)(unsigned int map_width, unsigned int map_height);
 

	
 
	/**
 
	 * The player is joining a multiplayer game.
 
	 *
 
	 * This is followed by event_enter_multiplayer() if the join was successful.
 
	 */
 
	void (*event_joining_multiplayer)();
 
};
 

	
 
/** Pointers supplied by OpenTTD, for the plugin to use. */
 
struct OpenTTD_SocialIntegration_v1_OpenTTDInfo {
 
	const char *openttd_version; ///< Version of OpenTTD. UTF-8, nul-terminated. OpenTTD is and remains the owner of the memory.
 
};
 

	
 
/** The result of the initialization. */
 
enum OpenTTD_SocialIntegration_v1_InitResult : int {
 
	OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS = 1, ///< Plugin initialized successfully.
 
	OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED = -1, ///< Plugin failed to initialize (generic error).
 
	OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING = -2, ///< The Social Platform is not running.
 
};
 

	
 
/**
 
 * Type of the Init function the plugin is expected to export from its dynamic library.
 
 *
 
 * The plugin has to export the implementation of this function as "SocialIntegration_vN_Init", where N is the API version this entry point is for.
 
 * A single plugin can have multiple versions implemented.
 
 *
 
 * @param[out] plugin_api    Structure the plugin must fill with pointers. Can contain nullptr if the plugin does not support a feature. The plugin is owner of the memory.
 
 * @param[in]  openttd_info  Structure that OpenTTD filled with pointers. All pointers will remain valid until shutdown(). OpenTTD is owner of the memory.
 
 * @return                   The status of the initialization.
 
 */
 
typedef OpenTTD_SocialIntegration_v1_InitResult (*OpenTTD_SocialIntegration_v1_Init)(OpenTTD_SocialIntegration_v1_PluginApi *plugin_api, const OpenTTD_SocialIntegration_v1_OpenTTDInfo *openttd_info);
 

	
 
/**
 
 * Type of the GetInfo function the plugin is expected to export from its dynamic library.
 
 *
 
 * The plugin has to export the implementation of this function as "SocialIntegration_vN_GetInfo", where N is the API version this entry point is for.
 
 * A single plugin can have multiple versions implemented.
 
 *
 
 * @param[out] plugin_info   Structure the plugin must fill with pointers. The plugin is owner of the memory.
 
 */
 
typedef void (*OpenTTD_SocialIntegration_v1_GetInfo)(OpenTTD_SocialIntegration_v1_PluginInfo *plugin_info);
 

	
 
#ifdef __cplusplus
 
} /* extern "C" */
 
#endif
 

	
 
#endif /* OPENTTD_SOCIAL_INTEGRATION_API_V1_H */
src/CMakeLists.txt
Show inline comments
 
@@ -379,23 +379,27 @@ add_files(
 
    ship_cmd.cpp
 
    ship_cmd.h
 
    ship_gui.cpp
 
    signal.cpp
 
    signal_func.h
 
    signal_type.h
 
    signature.cpp
 
    signature.h
 
    signs.cpp
 
    signs_base.h
 
    signs_cmd.cpp
 
    signs_cmd.h
 
    signs_func.h
 
    signs_gui.cpp
 
    signs_type.h
 
    slope_func.h
 
    slope_type.h
 
    smallmap_gui.cpp
 
    smallmap_gui.h
 
    social_integration.cpp
 
    social_integration.h
 
    sortlist_type.h
 
    sound.cpp
 
    sound_func.h
 
    sound_type.h
 
    sprite.cpp
 
    sprite.h
src/console_cmds.cpp
Show inline comments
 
@@ -2138,12 +2138,13 @@ DEF_CONSOLE_CMD(ConListDirs)
 
		{ SCENARIO_DIR,     "scenario",   false },
 
		{ HEIGHTMAP_DIR,    "heightmap",  false },
 
		/* Default save locations for user data */
 
		{ SAVE_DIR,         "save",       true  },
 
		{ AUTOSAVE_DIR,     "autosave",   true  },
 
		{ SCREENSHOT_DIR,   "screenshot", true  },
 
		{ SOCIAL_INTEGRATION_DIR, "social_integration", true },
 
	};
 

	
 
	if (argc != 2) {
 
		IConsolePrint(CC_HELP, "List all search paths or default directories for various categories.");
 
		IConsolePrint(CC_HELP, "Usage: list_dirs <category>");
 
		std::string cats = subdir_name_map[0].name;
src/crashlog.cpp
Show inline comments
 
@@ -119,12 +119,15 @@ void CrashLog::FillCrashLog()
 
		if (!this->TryExecute("compiler", [&info]() { SurveyCompiler(info["compiler"]); return true; })) {
 
			info["compiler"] = "crashed while gathering information";
 
		}
 
		if (!this->TryExecute("libraries", [&info]() { SurveyLibraries(info["libraries"]); return true; })) {
 
			info["libraries"] = "crashed while gathering information";
 
		}
 
		if (!this->TryExecute("plugins", [&info]() { SurveyPlugins(info["plugins"]); return true; })) {
 
			info["plugins"] = "crashed while gathering information";
 
		}
 
	}
 

	
 
	{
 
		auto &game = this->survey["game"];
 
		game["local_company"] = _local_company;
 
		game["current_company"] = _current_company;
src/fileio.cpp
Show inline comments
 
@@ -49,12 +49,13 @@ static const char * const _subdirs[] = {
 
	"lang" PATHSEP,
 
	"ai" PATHSEP,
 
	"ai" PATHSEP "library" PATHSEP,
 
	"game" PATHSEP,
 
	"game" PATHSEP "library" PATHSEP,
 
	"screenshot" PATHSEP,
 
	"social_integration" PATHSEP,
 
};
 
static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
 

	
 
/**
 
 * The search paths OpenTTD could search through.
 
 * At least one of the slots has to be filled with a path.
 
@@ -1051,13 +1052,13 @@ void DeterminePaths(const char *exe, boo
 
	FioCreateDirectory(_personal_dir);
 
#endif
 

	
 
	Debug(misc, 1, "{} found as personal directory", _personal_dir);
 

	
 
	static const Subdirectory default_subdirs[] = {
 
		SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
 
		SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR, SOCIAL_INTEGRATION_DIR
 
	};
 

	
 
	for (uint i = 0; i < lengthof(default_subdirs); i++) {
 
		FioCreateDirectory(_personal_dir + _subdirs[default_subdirs[i]]);
 
	}
 

	
 
@@ -1065,13 +1066,13 @@ void DeterminePaths(const char *exe, boo
 
	_searchpaths[SP_AUTODOWNLOAD_DIR] = _personal_dir + "content_download" PATHSEP;
 
	Debug(misc, 3, "{} added as search path", _searchpaths[SP_AUTODOWNLOAD_DIR]);
 
	FioCreateDirectory(_searchpaths[SP_AUTODOWNLOAD_DIR]);
 
	FillValidSearchPaths(only_local_path);
 

	
 
	/* Create the directory for each of the types of content */
 
	const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
 
	const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SOCIAL_INTEGRATION_DIR };
 
	for (uint i = 0; i < lengthof(dirs); i++) {
 
		FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
 
	}
 

	
 
	extern std::string _log_file;
 
	_log_file = _personal_dir + "openttd.log";
src/fileio_type.h
Show inline comments
 
@@ -118,12 +118,13 @@ enum Subdirectory {
 
	LANG_DIR,      ///< Subdirectory for all translation files
 
	AI_DIR,        ///< Subdirectory for all %AI files
 
	AI_LIBRARY_DIR,///< Subdirectory for all %AI libraries
 
	GAME_DIR,      ///< Subdirectory for all game scripts
 
	GAME_LIBRARY_DIR, ///< Subdirectory for all GS libraries
 
	SCREENSHOT_DIR,   ///< Subdirectory for all screenshots
 
	SOCIAL_INTEGRATION_DIR, ///< Subdirectory for all social integration plugins
 
	NUM_SUBDIRS,   ///< Number of subdirectories
 
	NO_DIRECTORY,  ///< A path without any base directory
 
};
 

	
 
/**
 
 * Types of searchpaths OpenTTD might use
src/lang/english.txt
Show inline comments
 
@@ -940,12 +940,14 @@ STR_GAME_OPTIONS_CAPTION                
 
STR_GAME_OPTIONS_TAB_GENERAL                                    :General
 
STR_GAME_OPTIONS_TAB_GENERAL_TT                                 :{BLACK}Choose general settings
 
STR_GAME_OPTIONS_TAB_GRAPHICS                                   :Graphics
 
STR_GAME_OPTIONS_TAB_GRAPHICS_TT                                :{BLACK}Choose graphics settings
 
STR_GAME_OPTIONS_TAB_SOUND                                      :Sound
 
STR_GAME_OPTIONS_TAB_SOUND_TT                                   :{BLACK}Choose sound and music settings
 
STR_GAME_OPTIONS_TAB_SOCIAL                                     :Social
 
STR_GAME_OPTIONS_TAB_SOCIAL_TT                                  :{BLACK}Choose social integration settings
 

	
 
STR_GAME_OPTIONS_VOLUME                                         :Volume
 
STR_GAME_OPTIONS_SFX_VOLUME                                     :Sound effects
 
STR_GAME_OPTIONS_MUSIC_VOLUME                                   :Music
 

	
 
STR_GAME_OPTIONS_VOLUME_0                                       :0%
 
@@ -1079,12 +1081,26 @@ STR_GAME_OPTIONS_BASE_SFX_TOOLTIP       
 
STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP                   :{BLACK}Additional information about the base sounds set
 

	
 
STR_GAME_OPTIONS_BASE_MUSIC                                     :{BLACK}Base music set
 
STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP                             :{BLACK}Select the base music set to use
 
STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP                 :{BLACK}Additional information about the base music set
 

	
 
STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE                            :{LTBLUE}(no plugins to integrate with social platforms installed)
 

	
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE                            :{BLACK}{RAW_STRING} ({RAW_STRING})
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM                         :{BLACK}Platform:
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE                            :{BLACK}Plugin state:
 

	
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_RUNNING                    :{GREEN}Running
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED                     :{RED}Failed to initialize
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_PLATFORM_NOT_RUNNING       :{ORANGE}{RAW_STRING} not running
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNLOADED                   :{RED}Unloaded
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_DUPLICATE                  :{RED}Duplicated plugin
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNSUPPORTED_API            :{RED}Unsupported version
 
STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_INVALID_SIGNATURE          :{RED}Invalid signature
 

	
 
STR_BASESET_STATUS                                              :{RAW_STRING} {RED}({NUM} missing/corrupted file{P "" s})
 

	
 
STR_ERROR_RESOLUTION_LIST_FAILED                                :{WHITE}Failed to retrieve a list of supported resolutions
 
STR_ERROR_FULLSCREEN_FAILED                                     :{WHITE}Fullscreen mode failed
 

	
 
# Custom currency window
src/network/network_client.cpp
Show inline comments
 
@@ -28,12 +28,13 @@
 
#include "network.h"
 
#include "network_base.h"
 
#include "network_client.h"
 
#include "network_gamelist.h"
 
#include "../core/backup_type.hpp"
 
#include "../thread.h"
 
#include "../social_integration.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "../safeguards.h"
 

	
 
/* This file handles all the client-commands */
 
@@ -842,12 +843,14 @@ NetworkRecvStatus ClientNetworkGameSocke
 
		}
 
	} else {
 
		/* take control over an existing company */
 
		SetLocalCompany(_network_join.company);
 
	}
 

	
 
	SocialIntegration::EventEnterMultiplayer(Map::SizeX(), Map::SizeY());
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p)
 
{
 
	if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
src/network/network_survey.cpp
Show inline comments
 
@@ -58,12 +58,13 @@ std::string NetworkSurveyHandler::Create
 
		SurveyOS(info["os"]);
 
		SurveyOpenTTD(info["openttd"]);
 
		SurveyConfiguration(info["configuration"]);
 
		SurveyFont(info["font"]);
 
		SurveyCompiler(info["compiler"]);
 
		SurveyLibraries(info["libraries"]);
 
		SurveyPlugins(info["plugins"]);
 
	}
 

	
 
	{
 
		auto &game = survey["game"];
 
		SurveyTimers(game["timers"]);
 
		SurveyCompanies(game["companies"]);
src/openttd.cpp
Show inline comments
 
@@ -72,12 +72,13 @@
 
#include "misc_cmd.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_game_economy.h"
 
#include "timer/timer_game_realtime.h"
 
#include "timer/timer_game_tick.h"
 
#include "social_integration.h"
 

	
 
#include "linkgraph/linkgraphschedule.h"
 

	
 
#include <system_error>
 

	
 
#include "safeguards.h"
 
@@ -285,12 +286,13 @@ static void ParseResolution(Dimension *r
 
static void ShutdownGame()
 
{
 
	IConsoleFree();
 

	
 
	if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
 

	
 
	SocialIntegration::Shutdown();
 
	DriverFactoryBase::ShutdownDrivers();
 

	
 
	UnInitWindowSystem();
 

	
 
	/* stop the scripts */
 
	AI::Uninitialize(false);
 
@@ -749,12 +751,13 @@ int openttd_main(int argc, char *argv[])
 
	/* Initialize the zoom level of the screen to normal */
 
	_screen.zoom = ZOOM_LVL_NORMAL;
 

	
 
	/* The video driver is now selected, now initialise GUI zoom */
 
	AdjustGUIZoom(false);
 

	
 
	SocialIntegration::Initialize();
 
	NetworkStartUp(); // initialize network-core
 

	
 
	if (!HandleBootstrap()) {
 
		ShutdownGame();
 
		return ret;
 
	}
 
@@ -994,12 +997,34 @@ bool SafeLoad(const std::string &filenam
 
		case GM_MENU:   LoadIntroGame();      break;
 
		case GM_EDITOR: MakeNewEditorWorld(); break;
 
	}
 
	return false;
 
}
 

	
 
static void UpdateSocialIntegration(GameMode game_mode)
 
{
 
	switch (game_mode) {
 
		case GM_BOOTSTRAP:
 
		case GM_MENU:
 
			SocialIntegration::EventEnterMainMenu();
 
			break;
 

	
 
		case GM_NORMAL:
 
			if (_networking) {
 
				SocialIntegration::EventEnterMultiplayer(Map::SizeX(), Map::SizeY());
 
			} else {
 
				SocialIntegration::EventEnterSingleplayer(Map::SizeX(), Map::SizeY());
 
			}
 
			break;
 

	
 
		case GM_EDITOR:
 
			SocialIntegration::EventEnterScenarioEditor(Map::SizeX(), Map::SizeY());
 
			break;
 
	}
 
}
 

	
 
void SwitchToMode(SwitchMode new_mode)
 
{
 
	/* If we are saving something, the network stays in its current state */
 
	if (new_mode != SM_SAVE_GAME) {
 
		/* If the network is active, make it not-active */
 
		if (_networking) {
 
@@ -1041,12 +1066,14 @@ void SwitchToMode(SwitchMode new_mode)
 
	if (new_mode != SM_SAVE_GAME) _switch_mode_time = std::chrono::steady_clock::now();
 

	
 
	switch (new_mode) {
 
		case SM_EDITOR: // Switch to scenario editor
 
			MakeNewEditorWorld();
 
			GenerateSavegameId();
 

	
 
			UpdateSocialIntegration(GM_EDITOR);
 
			break;
 

	
 
		case SM_RELOADGAME: // Reload with what-ever started the game
 
			if (_file_to_saveload.abstract_ftype == FT_SAVEGAME || _file_to_saveload.abstract_ftype == FT_SCENARIO) {
 
				/* Reload current savegame/scenario */
 
				_switch_mode = _game_mode == GM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
 
@@ -1058,18 +1085,22 @@ void SwitchToMode(SwitchMode new_mode)
 
				SwitchToMode(_switch_mode);
 
				break;
 
			}
 

	
 
			MakeNewGame(false, new_mode == SM_NEWGAME);
 
			GenerateSavegameId();
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 

	
 
		case SM_RESTARTGAME: // Restart --> 'Random game' with current settings
 
		case SM_NEWGAME: // New Game --> 'Random game'
 
			MakeNewGame(false, new_mode == SM_NEWGAME);
 
			GenerateSavegameId();
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 

	
 
		case SM_LOAD_GAME: { // Load game, Play Scenario
 
			ResetGRFConfig(true);
 
			ResetWindowSystem();
 

	
 
@@ -1081,27 +1112,33 @@ void SwitchToMode(SwitchMode new_mode)
 
					OnStartScenario();
 
				}
 
				OnStartGame(_network_dedicated);
 
				/* Decrease pause counter (was increased from opening load dialog) */
 
				Command<CMD_PAUSE>::Post(PM_PAUSED_SAVELOAD, false);
 
			}
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 
		}
 

	
 
		case SM_RESTART_HEIGHTMAP: // Load a heightmap and start a new game from it with current settings
 
		case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
 
			MakeNewGame(true, new_mode == SM_START_HEIGHTMAP);
 
			GenerateSavegameId();
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 

	
 
		case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
 
			SetLocalCompany(OWNER_NONE);
 

	
 
			GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
 
			GenerateSavegameId();
 
			MarkWholeScreenDirty();
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 

	
 
		case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
 
			if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) {
 
				SetLocalCompany(OWNER_NONE);
 
				GenerateSavegameId();
 
@@ -1109,18 +1146,22 @@ void SwitchToMode(SwitchMode new_mode)
 
				/* Cancel the saveload pausing */
 
				Command<CMD_PAUSE>::Post(PM_PAUSED_SAVELOAD, false);
 
			} else {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_CRITICAL);
 
			}
 

	
 
			UpdateSocialIntegration(GM_NORMAL);
 
			break;
 
		}
 

	
 
		case SM_JOIN_GAME: // Join a multiplayer game
 
			LoadIntroGame();
 
			NetworkClientJoinGame();
 

	
 
			SocialIntegration::EventJoiningMultiplayer();
 
			break;
 

	
 
		case SM_MENU: // Switch to game intro menu
 
			LoadIntroGame();
 
			if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) {
 
				ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL);
 
@@ -1131,12 +1172,14 @@ void SwitchToMode(SwitchMode new_mode)
 
				static bool asked_once = false;
 
				if (!asked_once) {
 
					asked_once = true;
 
					ShowNetworkAskSurvey();
 
				}
 
			}
 

	
 
			UpdateSocialIntegration(GM_MENU);
 
			break;
 

	
 
		case SM_SAVE_GAME: // Save game.
 
			/* Make network saved games on pause compatible to singleplayer mode */
 
			if (SaveOrLoad(_file_to_saveload.name, SLO_SAVE, DFT_GAME_FILE, NO_DIRECTORY) != SL_OK) {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
@@ -1541,7 +1584,8 @@ void GameLoop()
 
	}
 

	
 
	if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();
 

	
 
	SoundDriver::GetInstance()->MainLoop();
 
	MusicLoop();
 
	SocialIntegration::RunCallbacks();
 
}
src/settings_gui.cpp
Show inline comments
 
@@ -43,12 +43,13 @@
 
#include "mixer.h"
 
#include "newgrf_config.h"
 
#include "network/core/config.h"
 
#include "network/network_gui.h"
 
#include "network/network_survey.h"
 
#include "video/video_driver.hpp"
 
#include "social_integration.h"
 

	
 
#include "safeguards.h"
 

	
 

	
 
#if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
 
#	define HAS_TRUETYPE_FONT
 
@@ -167,12 +168,190 @@ static const std::map<int, StringID> _vo
 
	{  79, STR_NULL },
 
	{  95, STR_GAME_OPTIONS_VOLUME_75 },
 
	{  111, STR_NULL },
 
	{  127, STR_GAME_OPTIONS_VOLUME_100 },
 
};
 

	
 
static const NWidgetPart _nested_social_plugins_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_FRAME, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_TITLE), SetDataTip(STR_JUST_STRING2, STR_NULL),
 
			NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
 
				NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM, STR_NULL),
 
				NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_PLATFORM), SetMinimalSize(100, 12), SetDataTip(STR_JUST_RAW_STRING, STR_NULL), SetAlignment(SA_RIGHT),
 
			EndContainer(),
 
			NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
 
				NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE, STR_NULL),
 
				NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_STATE), SetMinimalSize(100, 12), SetDataTip(STR_JUST_STRING1, STR_NULL), SetAlignment(SA_RIGHT),
 
			EndContainer(),
 
		EndContainer(),
 
	EndContainer(),
 
};
 

	
 
static const NWidgetPart _nested_social_plugins_none_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE, STR_NULL),
 
	EndContainer(),
 
};
 

	
 
class NWidgetSocialPlugins : public NWidgetVertical {
 
public:
 
	NWidgetSocialPlugins()
 
	{
 
		this->plugins = SocialIntegration::GetPlugins();
 

	
 
		if (this->plugins.empty()) {
 
			auto widget = MakeNWidgets(std::begin(_nested_social_plugins_none_widgets), std::end(_nested_social_plugins_none_widgets), nullptr);
 
			this->Add(std::move(widget));
 
		} else {
 
			for (size_t i = 0; i < this->plugins.size(); i++) {
 
				auto widget = MakeNWidgets(std::begin(_nested_social_plugins_widgets), std::end(_nested_social_plugins_widgets), nullptr);
 
				this->Add(std::move(widget));
 
			}
 
		}
 

	
 
		this->SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0);
 
	}
 

	
 
	void FillWidgetLookup(WidgetLookup &widget_lookup) override
 
	{
 
		widget_lookup[WID_GO_SOCIAL_PLUGINS] = this;
 
		NWidgetVertical::FillWidgetLookup(widget_lookup);
 
	}
 

	
 
	void SetupSmallestSize(Window *w) override
 
	{
 
		this->current_index = -1;
 
		NWidgetVertical::SetupSmallestSize(w);
 
	}
 

	
 
	/**
 
	 * Find of all the plugins the one where the member is the widest (in pixels).
 
	 *
 
	 * @param member The member to check with.
 
	 * @return The plugin that has the widest value (in pixels) for the given member.
 
	 */
 
	template <typename T>
 
	std::string &GetWidestPlugin(T SocialIntegrationPlugin::*member) const
 
	{
 
		std::string *longest = &(this->plugins[0]->*member);
 
		int longest_length = 0;
 

	
 
		for (auto *plugin : this->plugins) {
 
			int length = GetStringBoundingBox(plugin->*member).width;
 
			if (length > longest_length) {
 
				longest_length = length;
 
				longest = &(plugin->*member);
 
			}
 
		}
 

	
 
		return *longest;
 
	}
 

	
 
	void SetStringParameters(int widget) const
 
	{
 
		switch (widget) {
 
			case WID_GO_SOCIAL_PLUGIN_TITLE:
 
				/* For SetupSmallestSize, use the longest string we have. */
 
				if (this->current_index < 0) {
 
					SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::name));
 
					SetDParamStr(1, GetWidestPlugin(&SocialIntegrationPlugin::version));
 
					break;
 
				}
 

	
 
				if (this->plugins[this->current_index]->name.empty()) {
 
					SetDParam(0, STR_JUST_RAW_STRING);
 
					SetDParamStr(1, this->plugins[this->current_index]->basepath);
 
				} else {
 
					SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE);
 
					SetDParamStr(1, this->plugins[this->current_index]->name);
 
					SetDParamStr(2, this->plugins[this->current_index]->version);
 
				}
 
				break;
 

	
 
			case WID_GO_SOCIAL_PLUGIN_PLATFORM:
 
				/* For SetupSmallestSize, use the longest string we have. */
 
				if (this->current_index < 0) {
 
					SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::social_platform));
 
					break;
 
				}
 

	
 
				SetDParamStr(0, this->plugins[this->current_index]->social_platform);
 
				break;
 

	
 
			case WID_GO_SOCIAL_PLUGIN_STATE: {
 
				static const std::pair<SocialIntegrationPlugin::State, StringID> state_to_string[] = {
 
					{ SocialIntegrationPlugin::RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_RUNNING },
 
					{ SocialIntegrationPlugin::FAILED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED },
 
					{ SocialIntegrationPlugin::PLATFORM_NOT_RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_PLATFORM_NOT_RUNNING },
 
					{ SocialIntegrationPlugin::UNLOADED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNLOADED },
 
					{ SocialIntegrationPlugin::DUPLICATE, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_DUPLICATE },
 
					{ SocialIntegrationPlugin::UNSUPPORTED_API, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNSUPPORTED_API },
 
					{ SocialIntegrationPlugin::INVALID_SIGNATURE, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_INVALID_SIGNATURE },
 
				};
 

	
 
				/* For SetupSmallestSize, use the longest string we have. */
 
				if (this->current_index < 0) {
 
					auto longest_plugin = GetWidestPlugin(&SocialIntegrationPlugin::social_platform);
 

	
 
					/* Set the longest plugin when looking for the longest status. */
 
					SetDParamStr(0, longest_plugin);
 

	
 
					StringID longest = STR_NULL;
 
					int longest_length = 0;
 
					for (auto state : state_to_string) {
 
						int length = GetStringBoundingBox(state.second).width;
 
						if (length > longest_length) {
 
							longest_length = length;
 
							longest = state.second;
 
						}
 
					}
 

	
 
					SetDParam(0, longest);
 
					SetDParamStr(1, longest_plugin);
 
					break;
 
				}
 

	
 
				auto plugin = this->plugins[this->current_index];
 

	
 
				/* Default string, in case no state matches. */
 
				SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED);
 
				SetDParamStr(1, plugin->social_platform);
 

	
 
				/* Find the string for the state. */
 
				for (auto state : state_to_string) {
 
					if (plugin->state == state.first) {
 
						SetDParam(0, state.second);
 
						break;
 
					}
 
				}
 
			}
 
			break;
 
		}
 
	}
 

	
 
	void Draw(const Window *w) override
 
	{
 
		this->current_index = 0;
 

	
 
		for (auto &wid : this->children) {
 
			wid->Draw(w);
 
			this->current_index++;
 
		}
 
	}
 

	
 
private:
 
	int current_index = -1;
 
	std::vector<SocialIntegrationPlugin *> plugins;
 
};
 

	
 
/** Construct nested container widget for managing the list of social plugins. */
 
std::unique_ptr<NWidgetBase> MakeNWidgetSocialPlugins()
 
{
 
	return std::make_unique<NWidgetSocialPlugins>();
 
}
 

	
 
struct GameOptionsWindow : Window {
 
	GameSettings *opt;
 
	bool reload;
 
	int gui_scale;
 
	static inline WidgetID active_tab = WID_GO_TAB_GENERAL;
 

	
 
@@ -345,12 +524,22 @@ struct GameOptionsWindow : Window {
 
					SetDParam(0, STR_GAME_OPTIONS_RESOLUTION_ITEM);
 
					SetDParam(1, _resolutions[current_resolution].width);
 
					SetDParam(2, _resolutions[current_resolution].height);
 
				}
 
				break;
 
			}
 

	
 
			case WID_GO_SOCIAL_PLUGIN_TITLE:
 
			case WID_GO_SOCIAL_PLUGIN_PLATFORM:
 
			case WID_GO_SOCIAL_PLUGIN_STATE: {
 
				const NWidgetSocialPlugins *plugin = this->GetWidget<NWidgetSocialPlugins>(WID_GO_SOCIAL_PLUGINS);
 
				assert(plugin != nullptr);
 

	
 
				plugin->SetStringParameters(widget);
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void DrawWidget(const Rect &r, WidgetID widget) const override
 
	{
 
		switch (widget) {
 
@@ -387,21 +576,22 @@ struct GameOptionsWindow : Window {
 
				break;
 
		}
 
	}
 

	
 
	void SetTab(WidgetID widget)
 
	{
 
		this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND);
 
		this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND, WID_GO_TAB_SOCIAL);
 
		this->LowerWidget(widget);
 
		GameOptionsWindow::active_tab = widget;
 

	
 
		int pane;
 
		switch (widget) {
 
			case WID_GO_TAB_GENERAL: pane = 0; break;
 
			case WID_GO_TAB_GRAPHICS: pane = 1; break;
 
			case WID_GO_TAB_SOUND: pane = 2; break;
 
			case WID_GO_TAB_SOCIAL: pane = 3; break;
 
			default: NOT_REACHED();
 
		}
 

	
 
		this->GetWidget<NWidgetStacked>(WID_GO_TAB_SELECTION)->SetDisplayedPlane(pane);
 
		this->SetDirty();
 
	}
 
@@ -490,12 +680,13 @@ struct GameOptionsWindow : Window {
 
			return;
 
		}
 
		switch (widget) {
 
			case WID_GO_TAB_GENERAL:
 
			case WID_GO_TAB_GRAPHICS:
 
			case WID_GO_TAB_SOUND:
 
			case WID_GO_TAB_SOCIAL:
 
				this->SetTab(widget);
 
				break;
 

	
 
			case WID_GO_SURVEY_PARTICIPATE_BUTTON:
 
				switch (_settings_client.network.participate_survey) {
 
					case PS_ASK:
 
@@ -811,12 +1002,13 @@ static constexpr NWidgetPart _nested_gam
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_GREY),
 
		NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPadding(WidgetDimensions::unscaled.sparse),
 
			NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GENERAL),  SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GENERAL, STR_GAME_OPTIONS_TAB_GENERAL_TT), SetFill(1, 0),
 
			NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GRAPHICS), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GRAPHICS, STR_GAME_OPTIONS_TAB_GRAPHICS_TT), SetFill(1, 0),
 
			NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOUND),    SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOUND, STR_GAME_OPTIONS_TAB_SOUND_TT), SetFill(1, 0),
 
			NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOCIAL),   SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOCIAL, STR_GAME_OPTIONS_TAB_SOCIAL_TT), SetFill(1, 0),
 
		EndContainer(),
 
	EndContainer(),
 
	NWidget(WWT_PANEL, COLOUR_GREY),
 
		NWidget(NWID_SELECTION, INVALID_COLOUR, WID_GO_TAB_SELECTION),
 
			/* General tab */
 
			NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.sparse), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
 
@@ -966,12 +1158,17 @@ static constexpr NWidgetPart _nested_gam
 
							NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GO_BASE_MUSIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP),
 
							NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_GO_BASE_MUSIC_TEXTFILE + TFT_LICENSE), SetFill(1, 0), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP),
 
						EndContainer(),
 
					EndContainer(),
 
				EndContainer(),
 
			EndContainer(),
 

	
 
			/* Social tab */
 
			NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.sparse), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
 
				NWidgetFunction(MakeNWidgetSocialPlugins),
 
			EndContainer(),
 
		EndContainer(),
 
	EndContainer(),
 
};
 

	
 
static WindowDesc _game_options_desc(__FILE__, __LINE__,
 
	WDP_CENTER, nullptr, 0, 0,
src/signature.cpp
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file signature.cpp Implementation of signature validation routines. */
 

	
 
#include "stdafx.h"
 

	
 
#include "signature.h"
 

	
 
#include "debug.h"
 
#include "fileio_func.h"
 
#include "string_func.h"
 

	
 
#include "3rdparty/monocypher/monocypher.h"
 
#include "3rdparty/monocypher/monocypher-ed25519.h"
 
#include "3rdparty/nlohmann/json.hpp"
 

	
 
#include "safeguards.h"
 

	
 
/** The public keys used for signature validation. */
 
static const std::initializer_list<std::array<uint8_t, 32>> _public_keys_v1 = {
 
	/* 2024-01-20 - Public key for Social Integration Plugins. */
 
	{ 0xed, 0x5d, 0x57, 0x47, 0x21, 0x99, 0x8b, 0x02, 0xdf, 0x6e, 0x3d, 0x69, 0xe1, 0x87, 0xca, 0xd0, 0x0e, 0x88, 0xc3, 0xe2, 0xb2, 0xa6, 0x7b, 0xc0, 0x42, 0xc8, 0xd6, 0x4b, 0x65, 0xe6, 0x48, 0xf7 },
 
};
 

	
 
/**
 
 * Calculate the 32-byte blake2b hash of a file.
 
 *
 
 * @param filename The filename to calculate the hash of.
 
 * @return The 32-byte blake2b hash of the file, hex-encoded.
 
 */
 
static std::string CalculateHashV1(const std::string &filename)
 
{
 
	FILE *f = FioFOpenFile(filename, "rb", NO_DIRECTORY);
 
	if (f == nullptr) {
 
		return "";
 
	}
 

	
 
	std::array<uint8_t, 32> digest;
 
	crypto_blake2b_ctx ctx;
 
	crypto_blake2b_init(&ctx, digest.size());
 

	
 
	while (!feof(f)) {
 
		std::array<uint8_t, 1024> buf;
 
		size_t len = fread(buf.data(), 1, buf.size(), f);
 

	
 
		crypto_blake2b_update(&ctx, buf.data(), len);
 
	}
 
	fclose(f);
 

	
 
	crypto_blake2b_final(&ctx, digest.data());
 
	return FormatArrayAsHex(digest);
 
}
 

	
 
/**
 
 * Validate whether the checksum of a file is the same.
 
 *
 
 * @param filename The filename to validate the checksum of.
 
 * @param checksum The expected checksum.
 
 * @return True iff the checksum of the file is the same as the expected checksum.
 
 */
 
static bool ValidateChecksum(const std::string &filename, const std::string &checksum)
 
{
 
	/* Checksums are "<version>$<hash>". Split out the version. */
 
	auto pos = checksum.find('$');
 
	assert(pos != std::string::npos); // Already validated by ValidateSchema().
 
	const std::string version = checksum.substr(0, pos);
 
	const std::string hash = checksum.substr(pos + 1);
 

	
 
	/* Calculate the checksum over the file. */
 
	std::string calculated_hash;
 
	if (version == "1") {
 
		calculated_hash = CalculateHashV1(filename);
 
	} else {
 
		Debug(misc, 0, "Failed to validate signature: unknown checksum version: {}", filename);
 
		return false;
 
	}
 

	
 
	/* Validate the checksum is the same. */
 
	if (calculated_hash.empty()) {
 
		Debug(misc, 0, "Failed to validate signature: couldn't calculate checksum for: {}", filename);
 
		return false;
 
	}
 
	if (calculated_hash != hash) {
 
		Debug(misc, 0, "Failed to validate signature: checksum mismatch for: {}", filename);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Validate whether the signature is valid for this set of files.
 
 *
 
 * @param signature The signature to validate.
 
 * @param files The files to validate the signature against.
 
 * @param filename The filename of the signatures file (for error-reporting).
 
 * @return True iff the signature is valid for this set of files.
 
 */
 
static bool ValidateSignature(const std::string &signature, const nlohmann::json &files, const std::string &filename)
 
{
 
	/* Signatures are "<version>$<signature>". Split out the version. */
 
	auto pos = signature.find('$');
 
	assert(pos != std::string::npos); // Already validated by ValidateSchema().
 
	const std::string version = signature.substr(0, pos);
 
	const std::string sig_value = signature.substr(pos + 1);
 

	
 
	/* Create the message we are going to validate. */
 
	std::string message = files.dump(-1);
 

	
 
	/* Validate the signature. */
 
	if (version == "1") {
 
		std::array<uint8_t, 64> sig;
 
		if (sig_value.size() != 128 || !ConvertHexToBytes(sig_value, sig)) {
 
			Debug(misc, 0, "Failed to validate signature: invalid signature: {}", filename);
 
			return false;
 
		}
 

	
 
		for (auto &pk_value : _public_keys_v1) {
 
			/* Check if the message is valid with this public key. */
 
			auto res = crypto_ed25519_check(sig.data(), pk_value.data(), reinterpret_cast<uint8_t *>(message.data()), message.size());
 
			if (res == 0) {
 
				return true;
 
			}
 
		}
 

	
 
		Debug(misc, 0, "Failed to validate signature: signature validation failed: {}", filename);
 
		return false;
 
	} else {
 
		Debug(misc, 0, "Failed to validate signature: unknown signature version: {}", filename);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Validate the signatures file complies with the JSON schema.
 
 *
 
 * @param signatures The signatures JSON to validate.
 
 * @param filename The filename of the signatures file (for error-reporting).
 
 * @return True iff the signatures file complies with the JSON schema.
 
 */
 
static bool ValidateSchema(const nlohmann::json &signatures, const std::string &filename)
 
{
 
	if (signatures["files"].is_null()) {
 
		Debug(misc, 0, "Failed to validate signature: no files found: {}", filename);
 
		return false;
 
	}
 

	
 
	if (signatures["signature"].is_null()) {
 
		Debug(misc, 0, "Failed to validate signature: no signature found: {}", filename);
 
		return false;
 
	}
 

	
 
	for (auto &signature : signatures["files"]) {
 
		if (signature["filename"].is_null() || signature["checksum"].is_null()) {
 
			Debug(misc, 0, "Failed to validate signature: invalid entry in files: {}", filename);
 
			return false;
 
		}
 

	
 
		const std::string sig_filename = signature["filename"];
 
		const std::string sig_checksum = signature["checksum"];
 

	
 
		if (sig_filename.empty() || sig_checksum.empty()) {
 
			Debug(misc, 0, "Failed to validate signature: invalid entry in files: {}", filename);
 
			return false;
 
		}
 

	
 
		auto pos = sig_checksum.find('$');
 
		if (pos == std::string::npos) {
 
			Debug(misc, 0, "Failed to validate signature: invalid checksum format: {}", filename);
 
			return false;
 
		}
 
	}
 

	
 
	const std::string signature = signatures["signature"];
 
	auto pos = signature.find('$');
 
	if (pos == std::string::npos) {
 
		Debug(misc, 0, "Failed to validate signature: invalid signature format: {}", filename);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Validate that the signatures mentioned in the signature file are matching
 
 * the files in question.
 
 *
 
 * @return True iff the files in the signature file passed validation.
 
 */
 
static bool _ValidateSignatureFile(const std::string &filename)
 
{
 
	size_t filesize;
 
	FILE *f = FioFOpenFile(filename, "rb", NO_DIRECTORY, &filesize);
 
	if (f == nullptr) {
 
		Debug(misc, 0, "Failed to validate signature: file not found: {}", filename);
 
		return false;
 
	}
 

	
 
	std::string text(filesize, '\0');
 
	size_t len = fread(text.data(), filesize, 1, f);
 
	if (len != 1) {
 
		Debug(misc, 0, "Failed to validate signature: failed to read file: {}", filename);
 
		return false;
 
	}
 

	
 
	nlohmann::json signatures;
 
	try {
 
		signatures = nlohmann::json::parse(text);
 
	} catch (nlohmann::json::exception &) {
 
		Debug(misc, 0, "Failed to validate signature: not a valid JSON file: {}", filename);
 
		return false;
 
	}
 

	
 
	/*
 
	 * The JSON file should look like:
 
	 *
 
	 *   {
 
	 *     "files": [
 
	 *       {
 
	 *         "checksum": "version$hash"
 
	 *         "filename": "filename",
 
	 *       },
 
	 *       ...
 
	 *     ],
 
	 *     "signature": "version$signature"
 
	 *   }
 
	 *
 
	 * The signature is a signed message of the content of "files", dumped as
 
	 * JSON without spaces / newlines, keys in the order as indicated above.
 
	 */
 

	
 
	if (!ValidateSchema(signatures, filename)) {
 
		return false;
 
	}
 

	
 
	if (!ValidateSignature(signatures["signature"], signatures["files"], filename)) {
 
		return false;
 
	}
 

	
 
	std::string dirname = std::filesystem::path(filename).parent_path().string();
 

	
 
	for (auto &signature : signatures["files"]) {
 
		const std::string sig_filename = dirname + PATHSEPCHAR + signature["filename"].get<std::string>();
 
		const std::string sig_checksum = signature["checksum"];
 

	
 
		if (!ValidateChecksum(sig_filename, sig_checksum)) {
 
			return false;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Validate that the signatures mentioned in the signature file are matching
 
 * the files in question.
 
 *
 
 * @note if ALLOW_INVALID_SIGNATURE is defined, this function will always
 
 * return true (but will still report any errors in the console).
 
 *
 
 * @return True iff the files in the signature file passed validation.
 
 */
 
bool ValidateSignatureFile(const std::string &filename)
 
{
 
	auto res = _ValidateSignatureFile(filename);;
 
#if defined(ALLOW_INVALID_SIGNATURE)
 
	(void)res; // Ignore the result.
 
	return true;
 
#else
 
	return res;
 
#endif
 
}
src/signature.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file signature.h Routines to validate signature files. */
 

	
 
#ifndef SIGNATURE_H
 
#define SIGNATURE_H
 

	
 
bool ValidateSignatureFile(const std::string &filename);
 

	
 
#endif /* SIGNATURE_H */
src/social_integration.cpp
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file social_integration.cpp Base implementation of social integration support. */
 

	
 
#include "stdafx.h"
 

	
 
#include "social_integration.h"
 
#include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
 

	
 
#include "debug.h"
 
#include "fileio_func.h"
 
#include "library_loader.h"
 
#include "rev.h"
 
#include "string_func.h"
 
#include "signature.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Container to track information per plugin.
 
 */
 
class InternalSocialIntegrationPlugin {
 
public:
 
	InternalSocialIntegrationPlugin(const std::string &filename, const std::string &basepath) : library(nullptr), external(basepath)
 
	{
 
		openttd_info.openttd_version = _openttd_revision;
 

	
 
		if (!ValidateSignatureFile(fmt::format("{}.sig", filename))) {
 
			external.state = SocialIntegrationPlugin::INVALID_SIGNATURE;
 
			return;
 
		}
 

	
 
		this->library = std::make_unique<LibraryLoader>(filename);
 
	}
 

	
 
	OpenTTD_SocialIntegration_v1_PluginInfo plugin_info = {}; ///< Information supplied by plugin.
 
	OpenTTD_SocialIntegration_v1_PluginApi plugin_api = {}; ///< API supplied by plugin.
 
	OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info = {}; ///< Information supplied by OpenTTD.
 

	
 
	std::unique_ptr<LibraryLoader> library = nullptr; ///< Library handle.
 

	
 
	SocialIntegrationPlugin external; ///< Information of the plugin to be used by other parts of our codebase.
 
};
 

	
 
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 Platform twice.
 

	
 
/** Helper for scanning for files with SocialIntegration as extension */
 
class SocialIntegrationFileScanner : FileScanner {
 
public:
 
	void Scan()
 
	{
 
#ifdef _WIN32
 
		std::string extension = "-social.dll";
 
#elif defined(__APPLE__)
 
		std::string extension = "-social.dylib";
 
#else
 
		std::string extension = "-social.so";
 
#endif
 

	
 
		this->FileScanner::Scan(extension.c_str(), SOCIAL_INTEGRATION_DIR, false);
 
	}
 

	
 
	bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
 
	{
 
		std::string basepath = filename.substr(basepath_length);
 
		Debug(misc, 1, "[Social Integration: {}] Loading ...", basepath);
 

	
 
		auto &plugin = _plugins.emplace_back(std::make_unique<InternalSocialIntegrationPlugin>(filename, basepath));
 

	
 
		/* Validation failed, so no library was loaded. */
 
		if (plugin->library == nullptr) {
 
			return false;
 
		}
 

	
 
		if (plugin->library->HasError()) {
 
			plugin->external.state = SocialIntegrationPlugin::FAILED;
 

	
 
			Debug(misc, 0, "[Social Integration: {}] Failed to load library: {}", basepath, plugin->library->GetLastError());
 
			return false;
 
		}
 

	
 
		OpenTTD_SocialIntegration_v1_GetInfo getinfo_func = plugin->library->GetFunction("SocialIntegration_v1_GetInfo");
 
		if (plugin->library->HasError()) {
 
			plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
 

	
 
			Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_GetInfo: {}", basepath, plugin->library->GetLastError());
 
			return false;
 
		}
 

	
 
		OpenTTD_SocialIntegration_v1_Init init_func = plugin->library->GetFunction("SocialIntegration_v1_Init");
 
		if (plugin->library->HasError()) {
 
			plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
 

	
 
			Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_Init: {}", basepath, plugin->library->GetLastError());
 
			return false;
 
		}
 

	
 
		getinfo_func(&plugin->plugin_info);
 
		/* Setup the information for the outside world to see. */
 
		plugin->external.social_platform = plugin->plugin_info.social_platform;
 
		plugin->external.name = plugin->plugin_info.name;
 
		plugin->external.version = plugin->plugin_info.version;
 

	
 
		/* Lowercase the string for comparison. */
 
		std::string lc_social_platform = plugin->plugin_info.social_platform;
 
		strtolower(lc_social_platform);
 

	
 
		/* Prevent more than one plugin for a certain Social Platform to be loaded, as that never ends well. */
 
		if (_loaded_social_platform.find(lc_social_platform) != _loaded_social_platform.end()) {
 
			plugin->external.state = SocialIntegrationPlugin::DUPLICATE;
 

	
 
			Debug(misc, 0, "[Social Integration: {}] Another plugin for {} is already loaded", basepath, plugin->plugin_info.social_platform);
 
			return false;
 
		}
 
		_loaded_social_platform.insert(lc_social_platform);
 

	
 
		auto state = init_func(&plugin->plugin_api, &plugin->openttd_info);
 
		switch (state) {
 
			case OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS:
 
				plugin->external.state = SocialIntegrationPlugin::RUNNING;
 

	
 
				Debug(misc, 1, "[Social Integration: {}] Loaded for {}: {} ({})", basepath, plugin->plugin_info.social_platform, plugin->plugin_info.name, plugin->plugin_info.version);
 
				return true;
 

	
 
			case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED:
 
				plugin->external.state = SocialIntegrationPlugin::FAILED;
 

	
 
				Debug(misc, 0, "[Social Integration: {}] Failed to initialize", basepath);
 
				return false;
 

	
 
			case OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING:
 
				plugin->external.state = SocialIntegrationPlugin::PLATFORM_NOT_RUNNING;
 

	
 
				Debug(misc, 1, "[Social Integration: {}] Failed to initialize: {} is not running", basepath, plugin->plugin_info.social_platform);
 
				return false;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
	}
 
};
 

	
 
std::vector<SocialIntegrationPlugin *> SocialIntegration::GetPlugins()
 
{
 
	std::vector<SocialIntegrationPlugin *> plugins;
 

	
 
	for (auto &plugin : _plugins) {
 
		plugins.push_back(&plugin->external);
 
	}
 

	
 
	return plugins;
 
}
 

	
 
void SocialIntegration::Initialize()
 
{
 
	SocialIntegrationFileScanner fs;
 
	fs.Scan();
 
}
 

	
 
/**
 
 * Wrapper to call a function pointer of a plugin if it isn't a nullptr.
 
 *
 
 * @param plugin Plugin to call the function pointer on.
 
 * @param func   Function pointer to call.
 
 */
 
template <typename T, typename... Ts>
 
static void PluginCall(std::unique_ptr<InternalSocialIntegrationPlugin> &plugin, T func, Ts... args)
 
{
 
	if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
 
		return;
 
	}
 

	
 
	if (func != nullptr) {
 
		func(args...);
 
	}
 
}
 

	
 
void SocialIntegration::Shutdown()
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.shutdown);
 
	}
 

	
 
	_plugins.clear();
 
	_loaded_social_platform.clear();
 
}
 

	
 
void SocialIntegration::RunCallbacks()
 
{
 
	for (auto &plugin : _plugins) {
 
		if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
 
			continue;
 
		}
 

	
 
		if (plugin->plugin_api.run_callbacks != nullptr) {
 
			if (!plugin->plugin_api.run_callbacks()) {
 
				Debug(misc, 1, "[Social Plugin: {}] Requested to be unloaded", plugin->external.basepath);
 

	
 
				_loaded_social_platform.erase(plugin->plugin_info.social_platform);
 
				plugin->external.state = SocialIntegrationPlugin::UNLOADED;
 
				PluginCall(plugin, plugin->plugin_api.shutdown);
 
			}
 
		}
 
	}
 
}
 

	
 
void SocialIntegration::EventEnterMainMenu()
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.event_enter_main_menu);
 
	}
 
}
 

	
 
void SocialIntegration::EventEnterScenarioEditor(uint map_width, uint map_height)
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.event_enter_scenario_editor, map_width, map_height);
 
	}
 
}
 

	
 
void SocialIntegration::EventEnterSingleplayer(uint map_width, uint map_height)
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.event_enter_singleplayer, map_width, map_height);
 
	}
 
}
 

	
 
void SocialIntegration::EventEnterMultiplayer(uint map_width, uint map_height)
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.event_enter_multiplayer, map_width, map_height);
 
	}
 
}
 

	
 
void SocialIntegration::EventJoiningMultiplayer()
 
{
 
	for (auto &plugin : _plugins) {
 
		PluginCall(plugin, plugin->plugin_api.event_joining_multiplayer);
 
	}
 
}
src/social_integration.h
Show inline comments
 
new file 100644
 
/*
 
 * 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 <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file social_integration.h Interface definitions for game to report/respond to social integration. */
 

	
 
#ifndef SOCIAL_INTEGRATION_H
 
#define SOCIAL_INTEGRATION_H
 

	
 
class SocialIntegrationPlugin {
 
public:
 
	enum State {
 
		RUNNING, ///< The plugin is successfully loaded and running.
 

	
 
		FAILED, ///< The plugin failed to initialize.
 
		PLATFORM_NOT_RUNNING, ///< The plugin failed to initialize because the Social Platform is not running.
 
		UNLOADED, ///< The plugin is unloaded upon request.
 
		DUPLICATE, ///< Another plugin of the same Social Platform is already loaded.
 
		UNSUPPORTED_API, ///< The plugin does not support the current API version.
 
		INVALID_SIGNATURE, ///< The signature of the plugin is invalid.
 
	};
 

	
 
	std::string basepath; ///< Base path of the plugin.
 

	
 
	std::string social_platform = "unknown"; ///< Social platform this plugin is for.
 
	std::string name = ""; ///< Name of the plugin.
 
	std::string version = ""; ///< Version of the plugin.
 

	
 
	State state = FAILED; ///< Result of the plugin's init function.
 

	
 
	SocialIntegrationPlugin(const std::string &basepath) : basepath(basepath) {}
 
};
 

	
 
class SocialIntegration {
 
public:
 
	/**
 
	 * Get the list of loaded social integration plugins.
 
	 */
 
	static std::vector<SocialIntegrationPlugin *> GetPlugins();
 

	
 
	/**
 
	 * Initialize the social integration system, loading any social integration plugins that are available.
 
	 */
 
	static void Initialize();
 

	
 
	/**
 
	 * Shutdown the social integration system, and all social integration plugins that are loaded.
 
	 */
 
	static void Shutdown();
 

	
 
	/**
 
	 * Allow any social integration library to handle their own events.
 
	 */
 
	static void RunCallbacks();
 

	
 
	/**
 
	 * Event: user entered the main menu.
 
	 */
 
	static void EventEnterMainMenu();
 

	
 
	/**
 
	 * Event: user entered the Scenario Editor.
 
	 */
 
	static void EventEnterScenarioEditor(uint map_width, uint map_height);
 

	
 
	/**
 
	 * Event: user entered a singleplayer game.
 
	 */
 
	static void EventEnterSingleplayer(uint map_width, uint map_height);
 

	
 
	/**
 
	 * Event: user entered a multiplayer game.
 
	 */
 
	static void EventEnterMultiplayer(uint map_width, uint map_height);
 

	
 
	/**
 
	 * Event: user is joining a multiplayer game.
 
	 */
 
	static void EventJoiningMultiplayer();
 
};
 

	
 
#endif /* SOCIAL_INTEGRATION_H */
src/survey.cpp
Show inline comments
 
@@ -31,12 +31,14 @@
 
#include "sound/sound_driver.hpp"
 
#include "video/video_driver.hpp"
 

	
 
#include "base_media_base.h"
 
#include "blitter/factory.hpp"
 

	
 
#include "social_integration.h"
 

	
 
#ifdef WITH_ALLEGRO
 
#	include <allegro.h>
 
#endif /* WITH_ALLEGRO */
 
#ifdef WITH_FONTCONFIG
 
#	include <fontconfig/fontconfig.h>
 
#endif /* WITH_FONTCONFIG */
 
@@ -78,12 +80,23 @@ NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, 
 
	{GRFStatus::GCS_DISABLED, "disabled"},
 
	{GRFStatus::GCS_NOT_FOUND, "not found"},
 
	{GRFStatus::GCS_INITIALISED, "initialised"},
 
	{GRFStatus::GCS_ACTIVATED, "activated"},
 
})
 

	
 
NLOHMANN_JSON_SERIALIZE_ENUM(SocialIntegrationPlugin::State, {
 
	{SocialIntegrationPlugin::State::RUNNING, "running"},
 
	{SocialIntegrationPlugin::State::FAILED, "failed"},
 
	{SocialIntegrationPlugin::State::PLATFORM_NOT_RUNNING, "platform_not_running"},
 
	{SocialIntegrationPlugin::State::UNLOADED, "unloaded"},
 
	{SocialIntegrationPlugin::State::DUPLICATE, "duplicate"},
 
	{SocialIntegrationPlugin::State::UNSUPPORTED_API, "unsupported_api"},
 
	{SocialIntegrationPlugin::State::INVALID_SIGNATURE, "invalid_signature"},
 
})
 

	
 

	
 
/** Lookup table to convert a VehicleType to a string. */
 
static const std::string _vehicle_type_to_string[] = {
 
	"train",
 
	"roadveh",
 
	"ship",
 
	"aircraft",
 
@@ -433,12 +446,32 @@ void SurveyLibraries(nlohmann::json &sur
 
	survey["curl"] = curl_v->version;
 
	survey["curl_ssl"] = curl_v->ssl_version == nullptr ? "none" : curl_v->ssl_version;
 
#endif
 
}
 

	
 
/**
 
 * Convert plugin information to JSON.
 
 *
 
 * @param survey The JSON object.
 
 */
 
void SurveyPlugins(nlohmann::json &survey)
 
{
 
	auto _plugins = SocialIntegration::GetPlugins();
 

	
 
	for (auto &plugin : _plugins) {
 
		auto &platform = survey[plugin->social_platform];
 
		platform.push_back({
 
			{"name", plugin->name},
 
			{"version", plugin->version},
 
			{"basepath", plugin->basepath},
 
			{"state", plugin->state},
 
		});
 
	}
 
}
 

	
 
/**
 
 * Change the bytes of memory into a textual version rounded up to the biggest unit.
 
 *
 
 * For example, 16751108096 would become 16 GiB.
 
 *
 
 * @param memory The bytes of memory.
 
 * @return std::string A textual representation.
src/survey.h
Show inline comments
 
@@ -18,12 +18,13 @@ void SurveyCompanies(nlohmann::json &sur
 
void SurveyCompiler(nlohmann::json &survey);
 
void SurveyConfiguration(nlohmann::json &survey);
 
void SurveyFont(nlohmann::json &survey);
 
void SurveyGameScript(nlohmann::json &survey);
 
void SurveyGrfs(nlohmann::json &survey);
 
void SurveyLibraries(nlohmann::json &survey);
 
void SurveyPlugins(nlohmann::json &survey);
 
void SurveyOpenTTD(nlohmann::json &survey);
 
void SurveySettings(nlohmann::json &survey, bool skip_if_default);
 
void SurveyTimers(nlohmann::json &survey);
 

	
 
/* Defined in os/<os>/survey_<os>.cpp. */
 
void SurveyOS(nlohmann::json &json);
src/widgets/settings_widget.h
Show inline comments
 
@@ -12,12 +12,13 @@
 

	
 
/** Widgets of the #GameOptionsWindow class. */
 
enum GameOptionsWidgets : WidgetID {
 
	WID_GO_TAB_GENERAL,            ///< General tab.
 
	WID_GO_TAB_GRAPHICS,           ///< Graphics tab.
 
	WID_GO_TAB_SOUND,              ///< Sound tab.
 
	WID_GO_TAB_SOCIAL,             ///< Social tab.
 
	WID_GO_TAB_SELECTION,          ///< Background of the tab selection.
 
	WID_GO_CURRENCY_DROPDOWN,      ///< Currency dropdown.
 
	WID_GO_DISTANCE_DROPDOWN,      ///< Measuring unit dropdown.
 
	WID_GO_AUTOSAVE_DROPDOWN,      ///< Dropdown to say how often to autosave.
 
	WID_GO_LANG_DROPDOWN,          ///< Language dropdown.
 
	WID_GO_RESOLUTION_DROPDOWN,    ///< Dropdown for the resolution.
 
@@ -50,12 +51,16 @@ enum GameOptionsWidgets : WidgetID {
 
	WID_GO_REFRESH_RATE_DROPDOWN,  ///< Dropdown for all available refresh rates.
 
	WID_GO_VIDEO_DRIVER_INFO,      ///< Label showing details about the current video driver.
 
	WID_GO_SURVEY_SEL,             ///< Selection to hide survey if no JSON library is compiled in.
 
	WID_GO_SURVEY_PARTICIPATE_BUTTON, ///< Toggle for participating in the automated survey.
 
	WID_GO_SURVEY_LINK_BUTTON,     ///< Button to open browser to go to the survey website.
 
	WID_GO_SURVEY_PREVIEW_BUTTON,  ///< Button to open a preview window with the survey results
 
	WID_GO_SOCIAL_PLUGINS,         ///< Main widget handling the social plugins.
 
	WID_GO_SOCIAL_PLUGIN_TITLE,    ///< Title of the frame of the social plugin.
 
	WID_GO_SOCIAL_PLUGIN_PLATFORM, ///< Platform of the social plugin.
 
	WID_GO_SOCIAL_PLUGIN_STATE,    ///< State of the social plugin.
 
};
 

	
 
/** Widgets of the #GameSettingsWindow class. */
 
enum GameSettingsWidgets : WidgetID {
 
	WID_GS_FILTER,             ///< Text filter.
 
	WID_GS_OPTIONSPANEL,       ///< Panel widget containing the option lists.
0 comments (0 inline, 0 general)