#include "stdafx.h" #include #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 "ControlManager.h" #include "ClientConnectionData.h" #include "EmulationSettings.h" #include "StandardController.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, shared_ptr 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->Pause(); RomInfo romInfo = _console->GetRomInfo(); GameInformationMessage gameInfo(romInfo.RomName, romInfo.Hash.Crc32, _controllerPort, _console->GetSettings()->CheckFlag(EmulationFlags::Paused)); SendNetMessage(gameInfo); SaveStateMessage saveState(_console); SendNetMessage(saveState); _console->Resume(); } 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()) { if(message->CheckPassword(_serverPassword, _connectionHash)) { _console->Pause(); _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->GetRomInfo().RomName.size() > 0) { SendGameInformation(); } _handshakeCompleted = true; RegisterNetPlayDevice(this, _controllerPort); GameServer::SendPlayerList(); _console->Resume(); } else { SendForceDisconnectMessage("The password you provided did not match - you have been disconnected."); } } else { SendForceDisconnectMessage("Server is using a different version of Mesen (" + EmulationSettings::GetMesenVersionString() + ") - 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->Pause(); 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->Resume(); } void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter) { switch(type) { case ConsoleNotificationType::GamePaused: case ConsoleNotificationType::GameResumed: case ConsoleNotificationType::GameReset: case ConsoleNotificationType::StateLoaded: case ConsoleNotificationType::CheatAdded: case ConsoleNotificationType::ConfigChanged: case ConsoleNotificationType::GameInitCompleted: SendGameInformation(); 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; }