Mesen-SX/Core/GameServerConnection.cpp
2020-12-19 23:30:09 +03:00

294 lines
7.5 KiB
C++

#include "stdafx.h"
#include <random>
#include "MessageManager.h"
#include "GameServerConnection.h"
#include "HandShakeMessage.h"
#include "InputDataMessage.h"
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "Console.h"
#include "BaseCartridge.h"
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "EmuSettings.h"
#include "SelectControllerMessage.h"
#include "PlayerListMessage.h"
#include "GameServer.h"
#include "ForceDisconnectMessage.h"
#include "BaseControlDevice.h"
#include "ServerInformationMessage.h"
GameServerConnection* GameServerConnection::_netPlayDevices[BaseControlDevice::PortCount] = {};
GameServerConnection::GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket,
string serverPassword) : GameConnection(console, socket)
{
//Server-side connection
_serverPassword = serverPassword;
_controllerPort = GameConnection::SpectatorPort;
SendServerInformation();
}
GameServerConnection::~GameServerConnection()
{
if (!_playerName.empty())
{
MessageManager::DisplayMessage(
"NetPlay", _playerName + " (Player " + std::to_string(_controllerPort + 1) + ") disconnected.");
}
UnregisterNetPlayDevice(this);
}
void GameServerConnection::SendServerInformation()
{
std::random_device rd;
std::mt19937 engine(rd());
std::uniform_int_distribution<> dist((int)' ', (int)'~');
string hash(50, ' ');
for (int i = 0; i < 50; i++)
{
int random = dist(engine);
hash[i] = (char)random;
}
_connectionHash = hash;
ServerInformationMessage message(hash);
SendNetMessage(message);
}
void GameServerConnection::SendGameInformation()
{
_console->Lock();
RomInfo romInfo = _console->GetRomInfo();
GameInformationMessage gameInfo(romInfo.RomFile.GetFileName(), _console->GetCartridge()->GetSha1Hash(),
_controllerPort, _console->IsPaused());
SendNetMessage(gameInfo);
SaveStateMessage saveState(_console);
SendNetMessage(saveState);
_console->Unlock();
}
void GameServerConnection::SendMovieData(uint8_t port, ControlDeviceState state)
{
if (_handshakeCompleted)
{
MovieDataMessage message(state, port);
SendNetMessage(message);
}
}
void GameServerConnection::SendForceDisconnectMessage(string disconnectMessage)
{
ForceDisconnectMessage message(disconnectMessage);
SendNetMessage(message);
Disconnect();
}
void GameServerConnection::PushState(ControlDeviceState state)
{
if (_inputData.size() == 0 || state != _inputData.back())
{
_inputData.clear();
_inputData.push_back(state);
}
}
ControlDeviceState GameServerConnection::GetState()
{
size_t inputBufferSize = _inputData.size();
ControlDeviceState stateData;
if (inputBufferSize > 0)
{
stateData = _inputData.front();
if (inputBufferSize > 1)
{
//Always keep the last one the client sent, it will be used until a new one is received
_inputData.pop_front();
}
}
return stateData;
}
void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
{
//Send the game's current state to the client and register the controller
if (message->IsValid(_console->GetSettings()->GetVersion()))
{
if (message->CheckPassword(_serverPassword, _connectionHash))
{
_console->Lock();
_controllerPort = message->IsSpectator() ? GameConnection::SpectatorPort : GetFirstFreeControllerPort();
_playerName = message->GetPlayerName();
string playerPortMessage = _controllerPort == GameConnection::SpectatorPort
? "Spectator"
: "Player " + std::to_string(_controllerPort + 1);
MessageManager::DisplayMessage("NetPlay", _playerName + " (" + playerPortMessage + ") connected.");
if (_console->GetCartridge())
{
SendGameInformation();
}
_handshakeCompleted = true;
RegisterNetPlayDevice(this, _controllerPort);
GameServer::SendPlayerList();
_console->Unlock();
}
else
{
SendForceDisconnectMessage("The password you provided did not match - you have been disconnected.");
}
}
else
{
SendForceDisconnectMessage(
"Server is using a different version of Mesen-S (" + _console->GetSettings()->GetVersionString() +
") - you have been disconnected.");
MessageManager::DisplayMessage("NetPlay", + "NetplayVersionMismatch", message->GetPlayerName());
}
}
void GameServerConnection::ProcessMessage(NetMessage* message)
{
switch (message->GetType())
{
case MessageType::HandShake:
ProcessHandshakeResponse((HandShakeMessage*)message);
break;
case MessageType::InputData:
if (!_handshakeCompleted)
{
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
PushState(((InputDataMessage*)message)->GetInputState());
break;
case MessageType::SelectController:
if (!_handshakeCompleted)
{
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
SelectControllerPort(((SelectControllerMessage*)message)->GetPortNumber());
break;
default:
break;
}
}
void GameServerConnection::SelectControllerPort(uint8_t port)
{
_console->Lock();
if (port == GameConnection::SpectatorPort)
{
//Client wants to be a spectator, make sure we are not using any controller
UnregisterNetPlayDevice(this);
_controllerPort = port;
}
else
{
GameServerConnection* netPlayDevice = GetNetPlayDevice(port);
if (netPlayDevice == this)
{
//Nothing to do, we're already this player
}
else if (netPlayDevice == nullptr)
{
//This port is free, we can switch
UnregisterNetPlayDevice(this);
RegisterNetPlayDevice(this, port);
_controllerPort = port;
}
else
{
//Another player is using this port, we can't use it
}
}
SendGameInformation();
GameServer::SendPlayerList();
_console->Unlock();
}
void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
switch (type)
{
case ConsoleNotificationType::GamePaused:
case ConsoleNotificationType::GameLoaded:
case ConsoleNotificationType::GameResumed:
case ConsoleNotificationType::GameReset:
case ConsoleNotificationType::StateLoaded:
case ConsoleNotificationType::CheatsChanged:
case ConsoleNotificationType::ConfigChanged:
SendGameInformation();
break;
case ConsoleNotificationType::BeforeEmulationStop:
{
//Make clients unload the current game
GameInformationMessage gameInfo("", "0000000000000000000000000000000000000000", _controllerPort, true);
SendNetMessage(gameInfo);
break;
}
default:
break;
}
}
void GameServerConnection::RegisterNetPlayDevice(GameServerConnection* device, uint8_t port)
{
GameServerConnection::_netPlayDevices[port] = device;
}
void GameServerConnection::UnregisterNetPlayDevice(GameServerConnection* device)
{
if (device != nullptr)
{
for (int i = 0; i < BaseControlDevice::PortCount; i++)
{
if (GameServerConnection::_netPlayDevices[i] == device)
{
GameServerConnection::_netPlayDevices[i] = nullptr;
break;
}
}
}
}
GameServerConnection* GameServerConnection::GetNetPlayDevice(uint8_t port)
{
return GameServerConnection::_netPlayDevices[port];
}
uint8_t GameServerConnection::GetFirstFreeControllerPort()
{
uint8_t hostPost = GameServer::GetHostControllerPort();
for (int i = 0; i < BaseControlDevice::PortCount; i++)
{
if (hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr)
{
return i;
}
}
return GameConnection::SpectatorPort;
}
string GameServerConnection::GetPlayerName()
{
return _playerName;
}
uint8_t GameServerConnection::GetControllerPort()
{
return _controllerPort;
}