mirror of
https://github.com/libretro/MesenCE.git
synced 2026-11-10 03:02:05 -10:00
* GB: Link cable support with two local consoles * GB: Fix crash when color blending is enabled * GB: Fix formatting * GB: Link UI fixes, control fixes, audio fixes Copied from Sour's branch. Also a separate change to use separate savefiles. * GB: Try to avoid crashes from buffer overflows * GB: Separate savefiles with linked multiplayer * GB: Add support for faster link speed * GB: Move enum to correct file * GB: Refactor, move serial transfer to function * -Fixed secondary console sometimes running far ahead of the main console when frame counters did not match (e.g because one console had their LCD disabled longer, etc.) This would cause buffer overflows in the audio buffer. -Fixed screen tearing when both consoles are not perfectly in sync (because of the LCD being disabled for a different amount of time, etc.) -Fixed main console not calling PlayQueuedAudio properly mid-frame -Reuse VS Dualsystem code to merge audio/video when possible/simple and moved that to AvMergeUtilities Some visual glitches can appear on the secondary screen (for GB) while rewinding, but this can't really be fixed. * Fixed warning (unused local variable) * Prevent loading single-console save states when in dual-console modes * Disable debugger for VS DualSystem's subconsole. Fixes issues when debugging VS DualSystem games * Code cleanup * Rework solution to disable debugger on second console to improve performance Added InternalRunFrame for nes & gb to avoid checking for the presence of the second console after each cpu instruction * Don't enable the linked GB option by default * Removed unused function * Netplay: Force game to reload after updating emulation settings when connecting to the server (or when the host changes game) This ensures that e.g the GB link cable setting is applied and that the game loads correctly, regardless of the client's link cable settings (otherwise the game could load without the link mode enabled, etc.) --------- Co-authored-by: Sour <[email protected]>
229 lines
6.5 KiB
C++
229 lines
6.5 KiB
C++
#include "pch.h"
|
|
#include <random>
|
|
#include "Netplay/GameServerConnection.h"
|
|
#include "Netplay/HandShakeMessage.h"
|
|
#include "Netplay/InputDataMessage.h"
|
|
#include "Netplay/MovieDataMessage.h"
|
|
#include "Netplay/GameInformationMessage.h"
|
|
#include "Netplay/SaveStateMessage.h"
|
|
#include "Netplay/SelectControllerMessage.h"
|
|
#include "Netplay/GameServer.h"
|
|
#include "Netplay/ForceDisconnectMessage.h"
|
|
#include "Netplay/ServerInformationMessage.h"
|
|
#include "Netplay/NetplayTypes.h"
|
|
#include "Shared/MessageManager.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/EmuSettings.h"
|
|
|
|
GameServerConnection::GameServerConnection(GameServer* gameServer, Emulator* emu, unique_ptr<Socket> socket, string serverPassword) : GameConnection(emu, std::move(socket))
|
|
{
|
|
//Server-side connection
|
|
_server = gameServer;
|
|
_serverPassword = serverPassword;
|
|
_controllerPort = NetplayControllerInfo { GameConnection::SpectatorPort, 0 };
|
|
SendServerInformation();
|
|
}
|
|
|
|
GameServerConnection::~GameServerConnection()
|
|
{
|
|
MessageManager::DisplayMessage("NetPlay", "Player disconnected.");
|
|
_server->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(bool forceReload)
|
|
{
|
|
auto lock = _emu->AcquireLock();
|
|
RomInfo romInfo = _emu->GetRomInfo();
|
|
GameInformationMessage gameInfo(_emu->GetSettings(), romInfo.RomFile.GetFileName(), _emu->GetCrc32(), _controllerPort, _emu->IsPaused());
|
|
SendNetMessage(gameInfo);
|
|
SaveStateMessage saveState(_emu, forceReload);
|
|
SendNetMessage(saveState);
|
|
_previousConfig = GetSerializedConfig();
|
|
}
|
|
|
|
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)
|
|
{
|
|
auto lock = _inputLock.AcquireSafe();
|
|
_inputData = state;
|
|
}
|
|
|
|
ControlDeviceState GameServerConnection::GetState()
|
|
{
|
|
ControlDeviceState stateData;
|
|
{
|
|
auto lock = _inputLock.AcquireSafe();
|
|
stateData = _inputData;
|
|
}
|
|
return stateData;
|
|
}
|
|
|
|
void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
|
|
{
|
|
//Send the game's current state to the client and register the controller
|
|
if(message->IsValid(_emu->GetSettings()->GetVersion())) {
|
|
if(message->CheckPassword(_serverPassword, _connectionHash)) {
|
|
auto lock = _emu->AcquireLock();
|
|
|
|
_controllerPort = message->IsSpectator() ? NetplayControllerInfo { GameConnection::SpectatorPort, 0 } : _server->GetFirstFreeControllerPort();
|
|
|
|
MessageManager::DisplayMessage("NetPlay", "Player connected.");
|
|
|
|
if(_emu->IsRunning()) {
|
|
SendGameInformation(true);
|
|
}
|
|
|
|
_handshakeCompleted = true;
|
|
_server->RegisterNetPlayDevice(this, _controllerPort);
|
|
_server->SendPlayerList();
|
|
} else {
|
|
SendForceDisconnectMessage("The password you provided did not match - you have been disconnected.");
|
|
}
|
|
} else {
|
|
SendForceDisconnectMessage("Server is using a different version of Mesen (" + _emu->GetSettings()->GetVersionString() + ") - you have been disconnected.");
|
|
MessageManager::DisplayMessage("NetPlay", +"NetplayVersionMismatch");
|
|
}
|
|
}
|
|
|
|
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)->GetController());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GameServerConnection::ProcessPendingEvents()
|
|
{
|
|
if(_needSendGameInfo) {
|
|
SendGameInformation(false);
|
|
_needSendGameInfo = false;
|
|
}
|
|
}
|
|
|
|
void GameServerConnection::SelectControllerPort(NetplayControllerInfo controller)
|
|
{
|
|
auto lock = _emu->AcquireLock();
|
|
if(controller.Port == GameConnection::SpectatorPort) {
|
|
//Client wants to be a spectator, make sure we are not using any controller
|
|
_server->UnregisterNetPlayDevice(this);
|
|
_controllerPort = controller;
|
|
} else {
|
|
GameServerConnection* netPlayDevice = _server->GetNetPlayDevice(controller);
|
|
if(netPlayDevice == this) {
|
|
//Nothing to do, we're already this player
|
|
} else if(netPlayDevice == nullptr) {
|
|
//This port is free, we can switch
|
|
_server->UnregisterNetPlayDevice(this);
|
|
_server->RegisterNetPlayDevice(this, controller);
|
|
_controllerPort = controller;
|
|
} else {
|
|
//Another player is using this port, we can't use it
|
|
}
|
|
}
|
|
SendGameInformation(false);
|
|
_server->SendPlayerList();
|
|
}
|
|
|
|
void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
|
{
|
|
switch(type) {
|
|
case ConsoleNotificationType::GameLoaded:
|
|
SendGameInformation(true);
|
|
break;
|
|
|
|
case ConsoleNotificationType::GamePaused:
|
|
case ConsoleNotificationType::GameResumed:
|
|
case ConsoleNotificationType::GameReset:
|
|
case ConsoleNotificationType::StateLoaded:
|
|
case ConsoleNotificationType::CheatsChanged:
|
|
SendGameInformation(false);
|
|
break;
|
|
|
|
case ConsoleNotificationType::PpuFrameDone: {
|
|
//Detect any configuration change that impacts emulation
|
|
//Send a save state to clients if any change is done
|
|
string cfg = GetSerializedConfig();
|
|
if(_previousConfig != cfg) {
|
|
_needSendGameInfo = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ConsoleNotificationType::BeforeEmulationStop: {
|
|
//Make clients unload the current game
|
|
GameInformationMessage gameInfo(nullptr, "", 0, _controllerPort, true);
|
|
SendNetMessage(gameInfo);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
NetplayControllerInfo GameServerConnection::GetControllerPort()
|
|
{
|
|
return _controllerPort;
|
|
}
|
|
|
|
string GameServerConnection::GetSerializedConfig()
|
|
{
|
|
Serializer s(0, true);
|
|
EmuSettings* settings = _emu->GetSettings();
|
|
s.Stream(*settings, "", -1);
|
|
stringstream cfg;
|
|
s.SaveTo(cfg, 0);
|
|
return cfg.str();
|
|
} |