Major network cleanup. Patch by Imarok.

Access the server from the client only, not from the GUI (except for
autostarted games).
Thereby lay the foundation for clients to setup the game (refs #3806)
and dedicated hosting (refs #3556).
Doesn't transfer nor remove the SetTurnLength showcase from 0ebe3486b6.

This was SVN commit r18322.
This commit is contained in:
elexis 2016-06-04 12:08:30 +00:00
parent 1871213496
commit 248a48d88a
9 changed files with 168 additions and 96 deletions

View File

@ -89,7 +89,9 @@ function reportDisconnect(reason, wasConnected)
function kickPlayer(username, ban)
{
if (!Engine.KickPlayer(username, ban))
if (g_IsController)
Engine.KickPlayer(username, ban);
else
addChatMessage({
"type": "system",
"text": sprintf(ban ? translate("Could not ban %(name)s.") : translate("Could not kick %(name)s."), {

View File

@ -656,7 +656,6 @@ function handleReadyMessage(message)
return;
g_PlayerAssignments[message.guid].status = +message.status == 1;
Engine.SetNetworkPlayerStatus(message.guid, +message.status);
updateReadyUI();
}

View File

@ -246,8 +246,8 @@ JS::Value GetEngineInfo(ScriptInterface::CxPrivate* pCxPrivate)
void StartNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
{
ENSURE(g_NetServer);
g_NetServer->StartGame();
ENSURE(g_NetClient);
g_NetClient->SendStartGameMessage();
}
void StartGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs, int playerID)
@ -330,14 +330,15 @@ void SaveGamePrefix(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring&
void SetNetworkGameAttributes(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs1)
{
ENSURE(g_NetServer);
ENSURE(g_NetClient);
//TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere
// (with no obvious reason).
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedValue attribs(cx, attribs1);
g_NetServer->UpdateGameAttributes(&attribs, *(pCxPrivate->pScriptInterface));
g_NetClient->SendGameSetupMessage(&attribs, *(pCxPrivate->pScriptInterface));
}
void StartNetworkHost(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& playerName)
@ -400,12 +401,11 @@ std::string GetPlayerGUID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
return g_NetClient->GetGUID();
}
bool KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& playerName, bool ban)
void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& playerName, bool ban)
{
if (!g_NetServer)
return false;
ENSURE(g_NetClient);
return g_NetServer->KickPlayer(playerName, ban);
g_NetClient->SendKickPlayerMessage(playerName, ban);
}
JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
@ -423,23 +423,16 @@ JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate)
void AssignNetworkPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int playerID, const std::string& guid)
{
ENSURE(g_NetServer);
ENSURE(g_NetClient);
g_NetServer->AssignPlayer(playerID, guid);
}
void SetNetworkPlayerStatus(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::string& guid, int ready)
{
ENSURE(g_NetServer);
g_NetServer->SetPlayerReady(guid, ready);
g_NetClient->SendAssignPlayerMessage(playerID, guid);
}
void ClearAllPlayerReady (ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
{
ENSURE(g_NetServer);
ENSURE(g_NetClient);
g_NetServer->ClearAllPlayerReady();
g_NetClient->SendClearAllReadyMessage();
}
void SendNetworkChat(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& message)
@ -1038,11 +1031,10 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, std::wstring, std::string, &StartNetworkJoin>("StartNetworkJoin");
scriptInterface.RegisterFunction<void, &DisconnectNetworkGame>("DisconnectNetworkGame");
scriptInterface.RegisterFunction<std::string, &GetPlayerGUID>("GetPlayerGUID");
scriptInterface.RegisterFunction<bool, CStrW, bool, &KickPlayer>("KickPlayer");
scriptInterface.RegisterFunction<void, CStrW, bool, &KickPlayer>("KickPlayer");
scriptInterface.RegisterFunction<JS::Value, &PollNetworkClient>("PollNetworkClient");
scriptInterface.RegisterFunction<void, JS::HandleValue, &SetNetworkGameAttributes>("SetNetworkGameAttributes");
scriptInterface.RegisterFunction<void, int, std::string, &AssignNetworkPlayer>("AssignNetworkPlayer");
scriptInterface.RegisterFunction<void, std::string, int, &SetNetworkPlayerStatus>("SetNetworkPlayerStatus");
scriptInterface.RegisterFunction<void, &ClearAllPlayerReady>("ClearAllPlayerReady");
scriptInterface.RegisterFunction<void, std::wstring, &SendNetworkChat>("SendNetworkChat");
scriptInterface.RegisterFunction<void, int, &SendNetworkReady>("SendNetworkReady");

View File

@ -323,6 +323,21 @@ void CNetClient::HandleDisconnect(u32 reason)
SetCurrState(NCS_UNCONNECTED);
}
void CNetClient::SendGameSetupMessage(JS::MutableHandleValue attrs, ScriptInterface& scriptInterface)
{
CGameSetupMessage gameSetup(scriptInterface);
gameSetup.m_Data = attrs;
SendMessage(&gameSetup);
}
void CNetClient::SendAssignPlayerMessage(const int playerID, const CStr& guid)
{
CAssignPlayerMessage assignPlayer;
assignPlayer.m_PlayerID = playerID;
assignPlayer.m_GUID = guid;
SendMessage(&assignPlayer);
}
void CNetClient::SendChatMessage(const std::wstring& text)
{
CChatMessage chat;
@ -337,12 +352,32 @@ void CNetClient::SendReadyMessage(const int status)
SendMessage(&readyStatus);
}
void CNetClient::SendClearAllReadyMessage()
{
CClearAllReadyMessage clearAllReady;
SendMessage(&clearAllReady);
}
void CNetClient::SendStartGameMessage()
{
CGameStartMessage gameStart;
SendMessage(&gameStart);
}
void CNetClient::SendRejoinedMessage()
{
CRejoinedMessage rejoinedMessage;
SendMessage(&rejoinedMessage);
}
void CNetClient::SendKickPlayerMessage(const CStrW& playerName, bool ban)
{
CKickedMessage kickPlayer;
kickPlayer.m_Name = playerName;
kickPlayer.m_Ban = ban;
SendMessage(&kickPlayer);
}
void CNetClient::SendPausedMessage(bool pause)
{
CClientPausedMessage pausedMessage;

View File

@ -187,16 +187,29 @@ public:
*/
void LoadFinished();
void SendGameSetupMessage(JS::MutableHandleValue attrs, ScriptInterface& scriptInterface);
void SendAssignPlayerMessage(const int playerID, const CStr& guid);
void SendChatMessage(const std::wstring& text);
void SendReadyMessage(const int status);
void SendClearAllReadyMessage();
void SendStartGameMessage();
/**
* Call when the client has rejoined a running match and finished
* the loading screen.
*/
void SendRejoinedMessage();
/**
* Call to kick/ban a client
*/
void SendKickPlayerMessage(const CStrW& playerName, bool ban);
/**
* Call when the client has paused or unpaused the game.
*/

View File

@ -203,6 +203,14 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
pNewMessage = new CSimulationMessage(scriptInterface);
break;
case NMT_CLEAR_ALL_READY:
pNewMessage = new CClearAllReadyMessage;
break;
case NMT_ASSIGN_PLAYER:
pNewMessage = new CAssignPlayerMessage;
break;
default:
LOGERROR("CNetMessageFactory::CreateMessage(): Unknown message type '%d' received", header.GetType());
break;

View File

@ -28,7 +28,7 @@
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!'
#define PS_PROTOCOL_VERSION 0x01010013 // Arbitrary protocol
#define PS_PROTOCOL_VERSION 0x01010014 // Arbitrary protocol
#define PS_DEFAULT_PORT 0x5073 // 'P', 's'
// Defines the list of message types. The order of the list must not change.
@ -50,7 +50,9 @@ enum NetMessageType
NMT_CHAT,
NMT_READY,
NMT_CLEAR_ALL_READY,
NMT_GAME_SETUP,
NMT_ASSIGN_PLAYER,
NMT_PLAYER_ASSIGNMENT,
NMT_FILE_TRANSFER_REQUEST,
@ -136,6 +138,9 @@ START_NMT_CLASS_(Ready, NMT_READY)
NMT_FIELD_INT(m_Status, u8, 1)
END_NMT_CLASS()
START_NMT_CLASS_(ClearAllReady, NMT_CLEAR_ALL_READY)
END_NMT_CLASS()
START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT)
NMT_START_ARRAY(m_Hosts)
NMT_FIELD(CStr, m_GUID)
@ -218,6 +223,11 @@ START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR)
NMT_END_ARRAY()
END_NMT_CLASS()
START_NMT_CLASS_(AssignPlayer, NMT_ASSIGN_PLAYER)
NMT_FIELD_INT(m_PlayerID, i8, 1)
NMT_FIELD(CStr, m_GUID)
END_NMT_CLASS()
END_NMTS()
#else

View File

@ -404,10 +404,7 @@ bool CNetServerWorker::RunStep()
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
std::vector<std::pair<int, CStr>> newAssignPlayer;
std::vector<bool> newStartGame;
std::vector<std::pair<CStr, int>> newPlayerReady;
std::vector<bool> newPlayerResetReady;
std::vector<std::string> newGameAttributes;
std::vector<u32> newTurnLength;
@ -418,22 +415,10 @@ bool CNetServerWorker::RunStep()
return false;
newStartGame.swap(m_StartGameQueue);
newPlayerReady.swap(m_PlayerReadyQueue);
newPlayerResetReady.swap(m_PlayerResetReadyQueue);
newAssignPlayer.swap(m_AssignPlayerQueue);
newGameAttributes.swap(m_GameAttributesQueue);
newTurnLength.swap(m_TurnLengthQueue);
}
for (const std::pair<int, CStr8>& p : newAssignPlayer)
AssignPlayer(p.first, p.second);
for (const std::pair<CStr8, int>& p : newPlayerReady)
SetPlayerReady(p.first, p.second);
if (!newPlayerResetReady.empty())
ClearAllPlayerReady();
if (!newGameAttributes.empty())
{
JS::RootedValue gameAttributesVal(cx);
@ -649,12 +634,19 @@ void CNetServerWorker::SetupSession(CNetServerSession* session)
session->AddTransition(NSS_PREGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_CHAT, NSS_PREGAME, (void*)&OnChat, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_READY, NSS_PREGAME, (void*)&OnReady, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_CLEAR_ALL_READY, NSS_PREGAME, (void*)&OnClearAllReady, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_GAME_SETUP, NSS_PREGAME, (void*)&OnGameSetup, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_ASSIGN_PLAYER, NSS_PREGAME, (void*)&OnAssignPlayer, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_KICKED, NSS_PREGAME, (void*)&OnKickPlayer, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_GAME_START, NSS_PREGAME, (void*)&OnStartGame, context);
session->AddTransition(NSS_PREGAME, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnLoadedGame, context);
session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_KICKED, NSS_JOIN_SYNCING, (void*)&OnKickPlayer, context);
session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnJoinSyncingLoadedGame, context);
session->AddTransition(NSS_INGAME, (uint)NMT_REJOINED, NSS_INGAME, (void*)&OnRejoined, context);
session->AddTransition(NSS_INGAME, (uint)NMT_KICKED, NSS_INGAME, (void*)&OnKickPlayer, context);
session->AddTransition(NSS_INGAME, (uint)NMT_CLIENT_PAUSED, NSS_INGAME, (void*)&OnClientPaused, context);
session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context);
@ -784,7 +776,7 @@ void CNetServerWorker::ClearAllPlayerReady()
SendPlayerAssignments();
}
bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban)
void CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban)
{
// Find the user with that name
std::vector<CNetServerSession*>::iterator it = std::find_if(m_Sessions.begin(), m_Sessions.end(),
@ -792,7 +784,7 @@ bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban)
// and return if no one or the host has that name
if (it == m_Sessions.end() || (*it)->GetGUID() == m_HostGUID)
return false;
return;
if (ban)
{
@ -814,8 +806,6 @@ bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban)
kickedMessage.m_Name = playerName;
kickedMessage.m_Ban = ban;
Broadcast(&kickedMessage);
return true;
}
void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid)
@ -1108,6 +1098,61 @@ bool CNetServerWorker::OnReady(void* context, CFsmEvent* event)
message->m_GUID = session->GetGUID();
server.Broadcast(message);
server.SetPlayerReady(message->m_GUID, message->m_Status);
return true;
}
bool CNetServerWorker::OnClearAllReady(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_CLEAR_ALL_READY);
CNetServerSession* session = (CNetServerSession*)context;
CNetServerWorker& server = session->GetServer();
if (session->GetGUID() == server.m_HostGUID)
server.ClearAllPlayerReady();
return true;
}
bool CNetServerWorker::OnGameSetup(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_GAME_SETUP);
CNetServerSession* session = (CNetServerSession*)context;
CNetServerWorker& server = session->GetServer();
if (session->GetGUID() == server.m_HostGUID)
{
CGameSetupMessage* message = (CGameSetupMessage*)event->GetParamRef();
server.UpdateGameAttributes(&(message->m_Data));
}
return true;
}
bool CNetServerWorker::OnAssignPlayer(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_ASSIGN_PLAYER);
CNetServerSession* session = (CNetServerSession*)context;
CNetServerWorker& server = session->GetServer();
if (session->GetGUID() == server.m_HostGUID)
{
CAssignPlayerMessage* message = (CAssignPlayerMessage*)event->GetParamRef();
server.AssignPlayer(message->m_PlayerID, message->m_GUID);
}
return true;
}
bool CNetServerWorker::OnStartGame(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_GAME_START);
CNetServerSession* session = (CNetServerSession*)context;
CNetServerWorker& server = session->GetServer();
if (session->GetGUID() == server.m_HostGUID)
server.StartGame();
return true;
}
@ -1204,6 +1249,21 @@ bool CNetServerWorker::OnRejoined(void* context, CFsmEvent* event)
return true;
}
bool CNetServerWorker::OnKickPlayer(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_KICKED);
CNetServerSession* session = (CNetServerSession*)context;
CNetServerWorker& server = session->GetServer();
if (session->GetGUID() == server.m_HostGUID)
{
CKickedMessage* message = (CKickedMessage*)event->GetParamRef();
server.KickPlayer(message->m_Name, message->m_Ban);
}
return true;
}
bool CNetServerWorker::OnDisconnect(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_CONNECTION_LOST);
@ -1372,30 +1432,6 @@ bool CNetServer::SetupConnection()
return m_Worker->SetupConnection();
}
bool CNetServer::KickPlayer(const CStrW& playerName, const bool ban)
{
CScopeLock lock(m_Worker->m_WorkerMutex);
return m_Worker->KickPlayer(playerName, ban);
}
void CNetServer::AssignPlayer(int playerID, const CStr& guid)
{
CScopeLock lock(m_Worker->m_WorkerMutex);
m_Worker->m_AssignPlayerQueue.emplace_back(playerID, guid);
}
void CNetServer::SetPlayerReady(const CStr& guid, int ready)
{
CScopeLock lock(m_Worker->m_WorkerMutex);
m_Worker->m_PlayerReadyQueue.emplace_back(guid, ready);
}
void CNetServer::ClearAllPlayerReady()
{
CScopeLock lock(m_Worker->m_WorkerMutex);
m_Worker->m_PlayerResetReadyQueue.push_back(false);
}
void CNetServer::StartGame()
{
CScopeLock lock(m_Worker->m_WorkerMutex);

View File

@ -115,31 +115,6 @@ public:
*/
bool SetupConnection();
/**
* Call from the GUI to update the player assignments.
* The given GUID will be (re)assigned to the given player ID.
* Any player currently using that ID will be unassigned.
* The changes will be asynchronously propagated to all clients.
*/
void AssignPlayer(int playerID, const CStr& guid);
/**
* Call from the GUI to update the player readiness.
* The changes will be asynchronously propagated to all clients.
*/
void SetPlayerReady(const CStr& guid, int ready);
/**
* Call from the GUI to set the all player readiness to 0.
* The changes will be asynchronously propagated to all clients.
*/
void ClearAllPlayerReady();
/**
* Disconnects a player from gamesetup or session.
*/
bool KickPlayer(const CStrW& playerName, const bool ban);
/**
* Call from the GUI to asynchronously notify all clients that they should start loading the game.
*/
@ -190,7 +165,7 @@ public:
/**
* Disconnects a player from gamesetup or session.
*/
bool KickPlayer(const CStrW& playerName, const bool ban);
void KickPlayer(const CStrW& playerName, const bool ban);
/**
* Send a message to all clients who have completed the full connection process
@ -270,9 +245,14 @@ private:
static bool OnInGame(void* context, CFsmEvent* event);
static bool OnChat(void* context, CFsmEvent* event);
static bool OnReady(void* context, CFsmEvent* event);
static bool OnClearAllReady(void* context, CFsmEvent* event);
static bool OnGameSetup(void* context, CFsmEvent* event);
static bool OnAssignPlayer(void* context, CFsmEvent* event);
static bool OnStartGame(void* context, CFsmEvent* event);
static bool OnLoadedGame(void* context, CFsmEvent* event);
static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event);
static bool OnRejoined(void* context, CFsmEvent* event);
static bool OnKickPlayer(void* context, CFsmEvent* event);
static bool OnDisconnect(void* context, CFsmEvent* event);
static bool OnClientPaused(void* context, CFsmEvent* event);
@ -367,10 +347,7 @@ private:
bool m_Shutdown; // protected by m_WorkerMutex
// Queues for messages sent by the game thread:
std::vector<std::pair<int, CStr>> m_AssignPlayerQueue; // protected by m_WorkerMutex
std::vector<bool> m_StartGameQueue; // protected by m_WorkerMutex
std::vector<std::pair<CStr, int>> m_PlayerReadyQueue; // protected by m_WorkerMutex
std::vector<bool> m_PlayerResetReadyQueue; // protected by m_WorkerMutex
std::vector<std::string> m_GameAttributesQueue; // protected by m_WorkerMutex
std::vector<u32> m_TurnLengthQueue; // protected by m_WorkerMutex
};