@@ -0,0 +1,117 @@
/* $Id$ */

/*
* 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_presence.cpp Base implementation of social presence support. */

#include "../stdafx.h"
#include <string>
#include "social_presence.h"
#include "social_plugin_api.h"


static bool _social_loaded = false;
static OpenTTD_SocialPluginApi _social_api{};
static OpenTTD_SocialPluginCallbacks _social_callbacks{};

static struct {
std::string server_name;
std::string server_cookie;
} _social_multiplayer_status;

/* Implemented by platform */
extern OpenTTD_SocialPluginInit SocialLoadPlugin();


static void Callback_handle_join_request(void *join_request_cookie, const char *friend_name)
{
SocialHandleJoinRequest(join_request_cookie, friend_name);
}

static void Callback_cancel_join_request(void *join_request_cookie)
{
SocialCancelJoinRequest(join_request_cookie);
}

static void Callback_join_requested_game(const char *server_cookie)
{
SocialJoinRequestedGame(server_cookie);
}


void SocialStartup()
{
if (_social_loaded) return;

OpenTTD_SocialPluginInit init = SocialLoadPlugin();

_social_callbacks.handle_join_request = Callback_handle_join_request;
_social_callbacks.cancel_join_request = Callback_cancel_join_request;
_social_callbacks.join_requested_game = Callback_join_requested_game;

if (init != nullptr) {
_social_loaded = init(OTTD_SOCIAL_PLUGIN_API_VERSION, &_social_api, &_social_callbacks) != 0;
}
}

void SocialShutdown()
{
if (_social_loaded) _social_api.shutdown();
_social_loaded = false;
}

void SocialEventLoop()
{
if (_social_loaded) _social_api.event_loop();
}

void SocialEnterSingleplayer()
{
if (_social_loaded) _social_api.enter_singleplayer();
}

void SocialBeginEnterMultiplayer(const std::string &server_name, const std::string &server_cookie)
{
_social_multiplayer_status.server_name = server_name;
_social_multiplayer_status.server_cookie = server_cookie;
}

void SocialCompleteEnterMultiplayer()
{
if (_social_loaded && !_social_multiplayer_status.server_cookie.empty()) {
_social_api.enter_multiplayer(_social_multiplayer_status.server_name.c_str(), _social_multiplayer_status.server_cookie.c_str());
}
}

void SocialEnterCompany(const std::string &company_name, CompanyID company_id)
{
if (_social_loaded) _social_api.enter_company(company_name.c_str(), company_id);
}

void SocialEnterSpectate()
{
if (_social_loaded) _social_api.enter_spectate();
}

void SocialExitGameplay()
{
if (_social_loaded) _social_api.exit_gameplay();
}

void SocialRespondJoinRequest(void *join_request_cookie, SocialJoinRequestResponse response)
{
OpenTTD_SocialPluginApi_JoinRequestResponse api_rsp;
switch (response) {
case SocialJoinRequestResponse::Ignore: api_rsp = OTTD_JRR_IGNORE; break;
case SocialJoinRequestResponse::Accept: api_rsp = OTTD_JRR_ACCEPT; break;
case SocialJoinRequestResponse::Reject: api_rsp = OTTD_JRR_REJECT; break;
default: return;
}

if (_social_loaded) _social_api.respond_join_request(join_request_cookie, api_rsp);
}
@@ -0,0 +1,57 @@
/* $Id$ */

/*
* 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_presence.h Interface definitions for game to report/respond to social media presence. */

#ifndef NETWORK_SOCIAL_PRESENCE_H
#define NETWORK_SOCIAL_PRESENCE_H

#include "../company_type.h"
#include <string>

enum class SocialJoinRequestResponse {
Accept,
Reject,
Ignore,
};

/* Functions implemented by social plug-in */

/** Main loop calls this to detect and initialise social plug-in */
void SocialStartup();
/** Main loop calls this to shut down social plug-in */
void SocialShutdown();
/** Main loop calls this, let social plug-in handle events */
void SocialEventLoop();
/** Game calls this when player starts/loads a singleplayer game */
void SocialEnterSingleplayer();
/** GUI calls this when player joins/starts a multiplayer */
void SocialBeginEnterMultiplayer(const std::string &server_name, const std::string &server_cookie);
/** Network code calls this when joining/starting a multiplayer game completes */
void SocialCompleteEnterMultiplayer();
/** Game calls this when player joins a company, or player's company changes name */
void SocialEnterCompany(const std::string &company_name, CompanyID company_id);
/** Game calls this when player enters spectate-mode */
void SocialEnterSpectate();
/** Game calls this when player leaves main gameplay mode */
void SocialExitGameplay();
/** Game calls this when player accepts a remote join request */
void SocialRespondJoinRequest(void *join_request_cookie, SocialJoinRequestResponse response);


/* Functions called from social plug-in on certain events */

/** Social plug-in calls this (from inside SocialEventLoop) if it receives a join request from a friend */
void SocialHandleJoinRequest(void *join_request_cookie, const std::string &friend_name);
/** Social plug-in calls this if a friend retracts a join request */
void SocialCancelJoinRequest(void *join_request_cookie);
/** Social plug-in calls this if the user received an accept on a join request */
void SocialJoinRequestedGame(const std::string &server_cookie);

#endif /* NETWORK_SOCIAL_PRESENCE_H */
@@ -66,6 +66,7 @@
#include "viewport_func.h"
#include "viewport_sprite_sorter.h"
#include "framerate_type.h"
#include "network/social_presence.h"

#include "linkgraph/linkgraphschedule.h"

@@ -288,6 +289,8 @@ static void ShutdownGame()
{
IConsoleFree();

SocialShutdown();

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

DriverFactoryBase::ShutdownDrivers();
@@ -850,6 +853,7 @@ int openttd_main(int argc, char *argv[])
WaitTillGeneratedWorld();

LoadIntroGame(false);
SocialStartup();

CheckForMissingGlyphs();

@@ -1077,6 +1081,7 @@ void SwitchToMode(SwitchMode new_mode)

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

@@ -1086,6 +1091,7 @@ void SwitchToMode(SwitchMode new_mode)
seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "Random Map");
}
MakeNewGame(false, new_mode == SM_NEWGAME);
if (!_networking) SocialEnterSingleplayer();
break;

case SM_LOAD_GAME: { // Load game, Play Scenario
@@ -1100,6 +1106,7 @@ void SwitchToMode(SwitchMode new_mode)
/* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */
EngineOverrideManager::ResetToCurrentNewGRFConfig();
}
if (!_networking) SocialEnterSingleplayer();
/* Update the local company for a loaded game. It is either always
* company #1 (eg 0) or in the case of a dedicated server a spectator */
SetLocalCompany(_network_dedicated ? COMPANY_SPECTATOR : COMPANY_FIRST);
@@ -1119,9 +1126,11 @@ void SwitchToMode(SwitchMode new_mode)
seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title);
}
MakeNewGame(true, true);
if (!_networking) SocialEnterSingleplayer();
break;

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

GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
@@ -1130,6 +1139,7 @@ void SwitchToMode(SwitchMode new_mode)

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)) {
SocialExitGameplay();
SetLocalCompany(OWNER_NONE);
_settings_newgame.game_creation.starting_year = _cur_year;
/* Cancel the saveload pausing */
@@ -1143,6 +1153,7 @@ void SwitchToMode(SwitchMode new_mode)

case SM_MENU: // Switch to game intro menu
LoadIntroGame();
SocialExitGameplay();
if (BaseSounds::ini_set == nullptr && BaseSounds::GetUsedSet()->fallback) {
ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL);
BaseSounds::ini_set = stredup(BaseSounds::GetUsedSet()->name);
@@ -1443,6 +1454,8 @@ void GameLoop()

ProcessAsyncSaveFinish();

SocialEventLoop();

/* autosave game? */
if (_do_autosave) {
DoAutosave();
@@ -19,6 +19,7 @@
#include "../../string_func.h"
#include "../../textbuf_gui.h"
#include "../../thread.h"
#include "../../network/social_plugin_api.h"

#include "table/strings.h"

@@ -217,3 +218,8 @@ void OSOpenBrowser(const char *url)
void SetCurrentThreadName(const char *)
{
}

OpenTTD_SocialPluginInit SocialLoadPlugin()
{
return nullptr;
}
@@ -18,6 +18,7 @@
#include "../../string_func.h"
#include "../../fios.h"
#include "../../thread.h"
#include "../../network/social_plugin_api.h"


#include <dirent.h>
@@ -299,3 +300,8 @@ void SetCurrentThreadName(const char *threadName) {
MacOSSetThreadName(threadName);
#endif /* defined(__APPLE__) */
}

OpenTTD_SocialPluginInit SocialLoadPlugin()
{
return nullptr;
}
@@ -31,6 +31,7 @@
#include <sys/stat.h>
#include "../../language.h"
#include "../../thread.h"
#include "../../network/social_plugin_api.h"

#include "../../safeguards.h"

@@ -822,3 +823,43 @@ void SetCurrentThreadName(const char *threadName)
#else
void SetCurrentThreadName(const char *) {}
#endif


OpenTTD_SocialPluginInit SocialLoadPlugin()
{
const HKEY root_keys[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };

for (HKEY root : root_keys) {
HKEY reg;
if (RegOpenKey(HKEY_CURRENT_USER, _T("SOFTWARE\\OpenTTD"), &reg) == ERROR_SUCCESS) {
DWORD value_type = REG_NONE;
TCHAR value[MAX_PATH]{};
DWORD value_len = sizeof(value); // yes size in bytes, not length
if (RegQueryValueEx(reg, _T("SocialPresencePlugin"), nullptr, &value_type, reinterpret_cast<LPBYTE>(value), &value_len) == ERROR_SUCCESS) {
RegCloseKey(reg);

TCHAR plugin_name[MAX_PATH]{};
value[lengthof(value) - 1] = _T('\0');
if (value_type == REG_EXPAND_SZ) {
TCHAR expand_value[MAX_PATH];
ExpandEnvironmentStrings(value, plugin_name, lengthof(plugin_name));
} else if (value_type == REG_SZ) {
MemCpyT(plugin_name, value, lengthof(plugin_name));
} else {
continue;
}

HMODULE plugin_library = LoadLibrary(plugin_name);
if (plugin_library != nullptr) {
OpenTTD_SocialPluginInit init_addr = (OpenTTD_SocialPluginInit)GetProcAddress(plugin_library, "SocialInit");
if (init_addr != nullptr) return init_addr;
FreeLibrary(plugin_library);
}
} else {
RegCloseKey(reg);
}
}
}

return nullptr;
}
@@ -24,6 +24,7 @@
#include "../window_gui.h"
#include "../window_func.h"
#include "../framerate_type.h"
#include "../network/social_presence.h"
#include "win32_v.h"
#include <windows.h>
#include <imm.h>