#include "stdafx.h" #include "GameClientConnection.h" #include "HandShakeMessage.h" #include "InputDataMessage.h" #include "MovieDataMessage.h" #include "GameInformationMessage.h" #include "SaveStateMessage.h" #include "Console.h" #include "EmuSettings.h" #include "ControlManager.h" #include "ClientConnectionData.h" #include "SnesController.h" #include "SelectControllerMessage.h" #include "PlayerListMessage.h" #include "ForceDisconnectMessage.h" #include "ServerInformationMessage.h" #include "NotificationManager.h" #include "RomFinder.h" GameClientConnection::GameClientConnection(shared_ptr console, shared_ptr socket, ClientConnectionData &connectionData) : GameConnection(console, socket) { _connectionData = connectionData; _shutdown = false; _enableControllers = false; _minimumQueueSize = 3; MessageManager::DisplayMessage("NetPlay", "ConnectedToServer"); } GameClientConnection::~GameClientConnection() { Shutdown(); } void GameClientConnection::Shutdown() { if(!_shutdown) { _shutdown = true; DisableControllers(); shared_ptr controlManager = _console->GetControlManager(); if(controlManager) { controlManager->UnregisterInputProvider(this); } MessageManager::DisplayMessage("NetPlay", "ConnectionLost"); _console->GetSettings()->ClearFlag(EmulationFlags::MaximumSpeed); } } void GameClientConnection::SendHandshake() { HandShakeMessage message(_connectionData.PlayerName, HandShakeMessage::GetPasswordHash(_connectionData.Password, _serverSalt), _connectionData.Spectator, _console->GetSettings()->GetVersion()); SendNetMessage(message); } void GameClientConnection::SendControllerSelection(uint8_t port) { SelectControllerMessage message(port); SendNetMessage(message); } void GameClientConnection::ClearInputData() { LockHandler lock = _writeLock.AcquireSafe(); for(int i = 0; i < BaseControlDevice::PortCount; i++) { _inputSize[i] = 0; _inputData[i].clear(); } } void GameClientConnection::ProcessMessage(NetMessage* message) { GameInformationMessage* gameInfo; switch(message->GetType()) { case MessageType::ServerInformation: _serverSalt = ((ServerInformationMessage*)message)->GetHashSalt(); SendHandshake(); break; case MessageType::SaveState: if(_gameLoaded) { DisableControllers(); _console->Lock(); ClearInputData(); ((SaveStateMessage*)message)->LoadState(_console); _enableControllers = true; InitControlDevice(); _console->Unlock(); } break; case MessageType::MovieData: if(_gameLoaded) { PushControllerState(((MovieDataMessage*)message)->GetPortNumber(), ((MovieDataMessage*)message)->GetInputState()); } break; case MessageType::ForceDisconnect: MessageManager::DisplayMessage("NetPlay", ((ForceDisconnectMessage*)message)->GetMessage()); break; case MessageType::PlayerList: _playerList = ((PlayerListMessage*)message)->GetPlayerList(); break; case MessageType::GameInformation: DisableControllers(); _console->Lock(); gameInfo = (GameInformationMessage*)message; if(gameInfo->GetPort() != _controllerPort) { _controllerPort = gameInfo->GetPort(); if(_controllerPort == GameConnection::SpectatorPort) { MessageManager::DisplayMessage("NetPlay", "ConnectedAsSpectator"); } else { MessageManager::DisplayMessage("NetPlay", "ConnectedAsPlayer", std::to_string(_controllerPort + 1)); } } ClearInputData(); _console->Unlock(); _gameLoaded = AttemptLoadGame(gameInfo->GetRomFilename(), gameInfo->GetSha1Hash()); if(!_gameLoaded) { _console->Stop(true); } else { _console->GetControlManager()->UnregisterInputProvider(this); _console->GetControlManager()->RegisterInputProvider(this); if(gameInfo->IsPaused()) { _console->Pause(); } else { _console->Resume(); } } break; default: break; } } bool GameClientConnection::AttemptLoadGame(string filename, string sha1Hash) { if(filename.size() > 0) { if(!RomFinder::LoadMatchingRom(_console.get(), filename, sha1Hash)) { MessageManager::DisplayMessage("NetPlay", "CouldNotFindRom", filename); return false; } else { return true; } } return false; } void GameClientConnection::PushControllerState(uint8_t port, ControlDeviceState state) { LockHandler lock = _writeLock.AcquireSafe(); _inputData[port].push_back(state); _inputSize[port]++; if(_inputData[port].size() >= _minimumQueueSize) { _waitForInput[port].Signal(); } } void GameClientConnection::DisableControllers() { //Used to prevent deadlocks when client is trying to fill its buffer while the host changes the current game/settings/etc. (i.e situations where we need to call Console::Pause()) _enableControllers = false; ClearInputData(); for(int i = 0; i < BaseControlDevice::PortCount; i++) { _waitForInput[i].Signal(); } } bool GameClientConnection::SetInput(BaseControlDevice *device) { if(_enableControllers) { uint8_t port = device->GetPort(); while(_inputSize[port] == 0) { _waitForInput[port].Wait(); if(port == 0 && _minimumQueueSize < 10) { //Increase buffer size - reduces freezes at the cost of additional lag _minimumQueueSize++; } if(_shutdown || !_enableControllers) { return true; } } LockHandler lock = _writeLock.AcquireSafe(); if(_shutdown || !_enableControllers || _inputSize[port] == 0) { return true; } ControlDeviceState state = _inputData[port].front(); _inputData[port].pop_front(); _inputSize[port]--; if(_inputData[port].size() > _minimumQueueSize) { //Too much data, catch up _console->GetSettings()->SetFlag(EmulationFlags::MaximumSpeed); } else { _console->GetSettings()->ClearFlag(EmulationFlags::MaximumSpeed); } device->SetRawState(state); } return true; } void GameClientConnection::InitControlDevice() { //Pretend we are using port 0 (to use player 1's keybindings during netplay) _newControlDevice = ControlManager::CreateControllerDevice(_console->GetSettings()->GetInputConfig().Controllers[_controllerPort].Type, 0, _console.get()); } void GameClientConnection::ProcessNotification(ConsoleNotificationType type, void* parameter) { if(type == ConsoleNotificationType::ConfigChanged) { InitControlDevice(); } else if(type == ConsoleNotificationType::GameLoaded) { _console->GetControlManager()->RegisterInputProvider(this); } } void GameClientConnection::SendInput() { if(_gameLoaded) { if(_newControlDevice) { _controlDevice = _newControlDevice; _newControlDevice.reset(); } ControlDeviceState inputState; if(_controlDevice) { _controlDevice->SetStateFromInput(); inputState = _controlDevice->GetRawState(); } if(_lastInputSent != inputState) { InputDataMessage message(inputState); SendNetMessage(message); _lastInputSent = inputState; } } } void GameClientConnection::SelectController(uint8_t port) { SendControllerSelection(port); } uint8_t GameClientConnection::GetAvailableControllers() { uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1; for(PlayerInfo &playerInfo : _playerList) { if(playerInfo.ControllerPort < BaseControlDevice::PortCount) { availablePorts &= ~(1 << playerInfo.ControllerPort); } } return availablePorts; } uint8_t GameClientConnection::GetControllerPort() { return _controllerPort; }