Netplay: Added support for netplay

This commit is contained in:
Sour 2019-10-20 20:05:39 -04:00
parent 83868031bb
commit 4f22eefa21
69 changed files with 3434 additions and 76 deletions

View file

@ -2,6 +2,7 @@
#include "CheatManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "NotificationManager.h"
#include "../Utilities/HexUtilities.h"
CheatManager::CheatManager(Console* console)
@ -33,6 +34,8 @@ void CheatManager::SetCheats(vector<CheatCode> codes)
} else if(hasCheats) {
MessageManager::DisplayMessage("Cheats", "CheatsDisabled");
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatsChanged);
}
void CheatManager::SetCheats(uint32_t codes[], uint32_t length)
@ -52,13 +55,18 @@ void CheatManager::ClearCheats(bool showMessage)
{
auto lock = _console->AcquireLock();
if(showMessage && !_cheats.empty()) {
MessageManager::DisplayMessage("Cheats", "CheatsDisabled");
}
bool hadCheats = !_cheats.empty();
_cheats.clear();
_hasCheats = false;
memset(_bankHasCheats, 0, sizeof(_bankHasCheats));
if(showMessage && hadCheats) {
MessageManager::DisplayMessage("Cheats", "CheatsDisabled");
//Used by net play
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatsChanged);
}
}
void CheatManager::AddStringCheat(string code)

View file

@ -0,0 +1,24 @@
#pragma once
#include "stdafx.h"
class ClientConnectionData
{
public:
string Host;
uint16_t Port;
string Password;
string PlayerName;
bool Spectator;
ClientConnectionData() {}
ClientConnectionData(string host, uint16_t port, string password, string playerName, bool spectator) :
Host(host), Port(port), Password(password), PlayerName(playerName), Spectator(spectator)
{
}
~ClientConnectionData()
{
}
};

View file

@ -40,7 +40,7 @@
Console::Console()
{
_settings.reset(new EmuSettings());
_settings.reset(new EmuSettings(this));
_paused = false;
_pauseOnNextFrame = false;
@ -340,8 +340,6 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom)
_ppu->PowerOn();
_cpu->PowerOn();
_notificationManager->SendNotification(ConsoleNotificationType::GameLoaded);
_rewindManager.reset(new RewindManager(shared_from_this()));
_notificationManager->RegisterNotificationListener(_rewindManager);
@ -349,6 +347,8 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom)
UpdateRegion();
_notificationManager->SendNotification(ConsoleNotificationType::GameLoaded);
_paused = false;
string modelName = _region == ConsoleRegion::Pal ? "PAL" : "NTSC";

View file

@ -146,20 +146,16 @@ void ControlManager::UpdateInputState()
//string log = "F: " + std::to_string(_console->GetPpu()->GetFrameCount()) + " C:" + std::to_string(_pollCounter) + " ";
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
device->ClearState();
device->SetStateFromInput();
bool inputSet = false;
for(size_t i = 0; i < _inputProviders.size(); i++) {
IInputProvider* provider = _inputProviders[i];
if(provider->SetInput(device.get())) {
inputSet = true;
break;
}
}
if(!inputSet) {
device->SetStateFromInput();
}
device->OnAfterSetState();
//log += "|" + device->GetTextState();
}

View file

@ -50,6 +50,7 @@
<ClInclude Include="BaseCoprocessor.h" />
<ClInclude Include="BatteryManager.h" />
<ClInclude Include="CheatManager.h" />
<ClInclude Include="ClientConnectionData.h" />
<ClInclude Include="Cpu.Shared.h" />
<ClInclude Include="CpuBwRamHandler.h" />
<ClInclude Include="CpuDebugger.h" />
@ -58,12 +59,21 @@
<ClInclude Include="Cx4Types.h" />
<ClInclude Include="DebugUtilities.h" />
<ClInclude Include="DmaControllerTypes.h" />
<ClInclude Include="ForceDisconnectMessage.h" />
<ClInclude Include="GameClient.h" />
<ClInclude Include="GameClientConnection.h" />
<ClInclude Include="GameConnection.h" />
<ClInclude Include="GameInformationMessage.h" />
<ClInclude Include="GameServer.h" />
<ClInclude Include="GameServerConnection.h" />
<ClInclude Include="Gsu.h" />
<ClInclude Include="GsuDebugger.h" />
<ClInclude Include="GsuDisUtils.h" />
<ClInclude Include="GsuRamHandler.h" />
<ClInclude Include="GsuRomHandler.h" />
<ClInclude Include="GsuTypes.h" />
<ClInclude Include="HandShakeMessage.h" />
<ClInclude Include="InputDataMessage.h" />
<ClInclude Include="InputHud.h" />
<ClInclude Include="InternalRegisterTypes.h" />
<ClInclude Include="MemoryMappings.h" />
@ -100,6 +110,8 @@
<ClInclude Include="LuaCallHelper.h" />
<ClInclude Include="LuaScriptingContext.h" />
<ClInclude Include="MemoryAccessCounter.h" />
<ClInclude Include="MessageType.h" />
<ClInclude Include="MovieDataMessage.h" />
<ClInclude Include="MovieTypes.h" />
<ClInclude Include="Multitap.h" />
<ClInclude Include="MesenMovie.h" />
@ -108,8 +120,10 @@
<ClInclude Include="NecDsp.h" />
<ClInclude Include="NecDspDisUtils.h" />
<ClInclude Include="NecDspTypes.h" />
<ClInclude Include="NetMessage.h" />
<ClInclude Include="NtscFilter.h" />
<ClInclude Include="Obc1.h" />
<ClInclude Include="PlayerListMessage.h" />
<ClInclude Include="PpuTools.h" />
<ClInclude Include="RecordedRomTest.h" />
<ClInclude Include="RegisterHandlerB.h" />
@ -148,12 +162,14 @@
<ClInclude Include="RegisterHandlerA.h" />
<ClInclude Include="RewindData.h" />
<ClInclude Include="RewindManager.h" />
<ClInclude Include="RomFinder.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="Sa1.h" />
<ClInclude Include="Sa1Cpu.h" />
<ClInclude Include="Sa1Types.h" />
<ClInclude Include="Sa1VectorHandler.h" />
<ClInclude Include="SaveStateManager.h" />
<ClInclude Include="SaveStateMessage.h" />
<ClInclude Include="ScaleFilter.h" />
<ClInclude Include="ScriptHost.h" />
<ClInclude Include="ScriptingContext.h" />
@ -162,6 +178,8 @@
<ClInclude Include="Sdd1Decomp.h" />
<ClInclude Include="Sdd1Mmc.h" />
<ClInclude Include="Sdd1Types.h" />
<ClInclude Include="SelectControllerMessage.h" />
<ClInclude Include="ServerInformationMessage.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="ShortcutKeyHandler.h" />
<ClInclude Include="SnesController.h" />
@ -218,6 +236,11 @@
<ClCompile Include="EmuSettings.cpp" />
<ClCompile Include="EventManager.cpp" />
<ClCompile Include="ExpressionEvaluator.cpp" />
<ClCompile Include="GameClient.cpp" />
<ClCompile Include="GameClientConnection.cpp" />
<ClCompile Include="GameConnection.cpp" />
<ClCompile Include="GameServer.cpp" />
<ClCompile Include="GameServerConnection.cpp" />
<ClCompile Include="Gsu.cpp" />
<ClCompile Include="Gsu.Instructions.cpp" />
<ClCompile Include="GsuDebugger.cpp" />

View file

@ -422,6 +422,60 @@
<ClInclude Include="SpcFileData.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="GameClient.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="GameClientConnection.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="GameConnection.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="GameServerConnection.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="ServerInformationMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="SaveStateMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="PlayerListMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="NetMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="HandShakeMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="GameInformationMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="ForceDisconnectMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="SelectControllerMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="MovieDataMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="InputDataMessage.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="MessageType.h">
<Filter>Netplay\Messages</Filter>
</ClInclude>
<ClInclude Include="ClientConnectionData.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="GameServer.h">
<Filter>Netplay</Filter>
</ClInclude>
<ClInclude Include="RomFinder.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -689,6 +743,21 @@
<ClCompile Include="SpcHud.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="GameClient.cpp">
<Filter>Netplay</Filter>
</ClCompile>
<ClCompile Include="GameClientConnection.cpp">
<Filter>Netplay</Filter>
</ClCompile>
<ClCompile Include="GameConnection.cpp">
<Filter>Netplay</Filter>
</ClCompile>
<ClCompile Include="GameServerConnection.cpp">
<Filter>Netplay</Filter>
</ClCompile>
<ClCompile Include="GameServer.cpp">
<Filter>Netplay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -751,5 +820,11 @@
<Filter Include="Movies">
<UniqueIdentifier>{e6a0a09a-5e38-4a63-83bf-095f297b7633}</UniqueIdentifier>
</Filter>
<Filter Include="Netplay">
<UniqueIdentifier>{d825d20e-0fac-4667-afbc-da08f15df645}</UniqueIdentifier>
</Filter>
<Filter Include="Netplay\Messages">
<UniqueIdentifier>{c58f689e-a6af-4b9c-bb77-0be9d2b1142c}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -3,10 +3,13 @@
#include "EmuSettings.h"
#include "KeyManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "NotificationManager.h"
#include "../Utilities/FolderUtilities.h"
EmuSettings::EmuSettings()
EmuSettings::EmuSettings(Console* console)
{
_console = console;
_flags = 0;
_debuggerFlags = 0;
_inputConfigVersion = 0;
@ -62,8 +65,18 @@ AudioConfig EmuSettings::GetAudioConfig()
void EmuSettings::SetInputConfig(InputConfig config)
{
bool controllersChanged = false;
for(int i = 0; i < 5; i++) {
controllersChanged |= _input.Controllers[i].Type != config.Controllers[i].Type;
}
_input = config;
_inputConfigVersion++;
if(controllersChanged) {
//Used by net play
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ConfigChanged);
}
}
InputConfig EmuSettings::GetInputConfig()
@ -78,8 +91,8 @@ uint32_t EmuSettings::GetInputConfigVersion()
void EmuSettings::SetEmulationConfig(EmulationConfig config)
{
bool prevOverclockEnabled = _emulation.PpuExtraScanlinesAfterNmi > 0 || _emulation.PpuExtraScanlinesBeforeNmi > 0;
bool overclockEnabled = config.PpuExtraScanlinesAfterNmi > 0 || config.PpuExtraScanlinesBeforeNmi > 0;
bool prevOverclockEnabled = _emulation.PpuExtraScanlinesAfterNmi > 0 || _emulation.PpuExtraScanlinesBeforeNmi > 0 || _emulation.GsuClockSpeed > 100;
bool overclockEnabled = config.PpuExtraScanlinesAfterNmi > 0 || config.PpuExtraScanlinesBeforeNmi > 0 || config.GsuClockSpeed > 100;
_emulation = config;
if(prevOverclockEnabled != overclockEnabled) {
@ -88,6 +101,9 @@ void EmuSettings::SetEmulationConfig(EmulationConfig config)
} else {
MessageManager::DisplayMessage("Overclock", "OverclockDisabled");
}
//Used by net play
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ConfigChanged);
}
}

View file

@ -2,9 +2,13 @@
#include "stdafx.h"
#include "SettingTypes.h"
class Console;
class EmuSettings
{
private:
Console* _console;
VideoConfig _video;
AudioConfig _audio;
InputConfig _input;
@ -30,7 +34,7 @@ private:
void SetShortcutKey(EmulatorShortcut shortcut, KeyCombination keyCombination, int keySetIndex);
public:
EmuSettings();
EmuSettings(Console* console);
uint32_t GetVersion();
string GetVersionString();

View file

@ -0,0 +1,32 @@
#pragma once
#include "stdafx.h"
#include "MessageManager.h"
#include "NetMessage.h"
#include "Console.h"
#include "../Utilities/FolderUtilities.h"
class ForceDisconnectMessage : public NetMessage
{
private:
char* _disconnectMessage = nullptr;
uint32_t _messageLength = 0;
protected:
virtual void ProtectedStreamState()
{
StreamArray((void**)&_disconnectMessage, _messageLength);
}
public:
ForceDisconnectMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
ForceDisconnectMessage(string message) : NetMessage(MessageType::ForceDisconnect)
{
CopyString(&_disconnectMessage, _messageLength, message);
}
string GetMessage()
{
return _disconnectMessage;
}
};

120
Core/GameClient.cpp Normal file
View file

@ -0,0 +1,120 @@
#include "stdafx.h"
#include <thread>
using std::thread;
#include "MessageManager.h"
#include "GameClient.h"
#include "Console.h"
#include "NotificationManager.h"
#include "../Utilities/Socket.h"
#include "ClientConnectionData.h"
#include "GameClientConnection.h"
shared_ptr<GameClient> GameClient::_instance;
GameClient::GameClient(shared_ptr<Console> console)
{
_console = console;
_stop = false;
}
GameClient::~GameClient()
{
_stop = true;
if(_clientThread) {
_clientThread->join();
}
}
bool GameClient::Connected()
{
shared_ptr<GameClient> instance = _instance;
return instance ? instance->_connected : false;
}
void GameClient::Connect(shared_ptr<Console> console, ClientConnectionData &connectionData)
{
_instance.reset(new GameClient(console));
console->GetNotificationManager()->RegisterNotificationListener(_instance);
shared_ptr<GameClient> instance = _instance;
if(instance) {
instance->PrivateConnect(connectionData);
instance->_clientThread.reset(new thread(&GameClient::Exec, instance.get()));
}
}
void GameClient::Disconnect()
{
_instance.reset();
}
shared_ptr<GameClientConnection> GameClient::GetConnection()
{
shared_ptr<GameClient> instance = _instance;
return instance ? instance->_connection : nullptr;
}
void GameClient::PrivateConnect(ClientConnectionData &connectionData)
{
_stop = false;
shared_ptr<Socket> socket(new Socket());
if(socket->Connect(connectionData.Host.c_str(), connectionData.Port)) {
_connection.reset(new GameClientConnection(_console, socket, connectionData));
_console->GetNotificationManager()->RegisterNotificationListener(_connection);
_connected = true;
} else {
MessageManager::DisplayMessage("NetPlay", "CouldNotConnect");
_connected = false;
}
}
void GameClient::Exec()
{
if(_connected) {
while(!_stop) {
if(!_connection->ConnectionError()) {
_connection->ProcessMessages();
_connection->SendInput();
} else {
_connected = false;
_connection->Shutdown();
_connection.reset();
break;
}
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
}
}
}
void GameClient::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::GameLoaded &&
std::this_thread::get_id() != _clientThread->get_id() &&
std::this_thread::get_id() != _console->GetEmulationThreadId()
) {
//Disconnect if the client tried to manually load a game
//A deadlock occurs if this is called from the emulation thread while a network message is being processed
GameClient::Disconnect();
}
}
void GameClient::SelectController(uint8_t port)
{
shared_ptr<GameClientConnection> connection = GetConnection();
if(connection) {
connection->SelectController(port);
}
}
uint8_t GameClient::GetAvailableControllers()
{
shared_ptr<GameClientConnection> connection = GetConnection();
return connection ? connection->GetAvailableControllers() : 0;
}
uint8_t GameClient::GetControllerPort()
{
shared_ptr<GameClientConnection> connection = GetConnection();
return connection ? connection->GetControllerPort() : GameConnection::SpectatorPort;
}

42
Core/GameClient.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "INotificationListener.h"
using std::thread;
class Socket;
class GameClientConnection;
class ClientConnectionData;
class Console;
class GameClient : public INotificationListener
{
private:
static shared_ptr<GameClient> _instance;
shared_ptr<Console> _console;
unique_ptr<thread> _clientThread;
atomic<bool> _stop;
shared_ptr<GameClientConnection> _connection;
bool _connected = false;
static shared_ptr<GameClientConnection> GetConnection();
void PrivateConnect(ClientConnectionData &connectionData);
void Exec();
public:
GameClient(shared_ptr<Console> console);
virtual ~GameClient();
static bool Connected();
static void Connect(shared_ptr<Console> console, ClientConnectionData &connectionData);
static void Disconnect();
static void SelectController(uint8_t port);
static uint8_t GetControllerPort();
static uint8_t GetAvailableControllers();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
};

View file

@ -0,0 +1,267 @@
#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> console, shared_ptr<Socket> 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> 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())
ClearInputData();
_enableControllers = false;
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();
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;
}

View file

@ -0,0 +1,62 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "GameConnection.h"
#include "../Utilities/AutoResetEvent.h"
#include "../Utilities/SimpleLock.h"
#include "BaseControlDevice.h"
#include "INotificationListener.h"
#include "IInputProvider.h"
#include "ControlDeviceState.h"
#include "ClientConnectionData.h"
class Console;
class GameClientConnection : public GameConnection, public INotificationListener, public IInputProvider
{
private:
std::deque<ControlDeviceState> _inputData[BaseControlDevice::PortCount];
atomic<uint32_t> _inputSize[BaseControlDevice::PortCount];
AutoResetEvent _waitForInput[BaseControlDevice::PortCount];
SimpleLock _writeLock;
atomic<bool> _shutdown;
atomic<bool> _enableControllers;
atomic<uint32_t> _minimumQueueSize;
vector<PlayerInfo> _playerList;
shared_ptr<BaseControlDevice> _controlDevice;
shared_ptr<BaseControlDevice> _newControlDevice;
ControlDeviceState _lastInputSent;
bool _gameLoaded = false;
uint8_t _controllerPort = GameConnection::SpectatorPort;
ClientConnectionData _connectionData;
string _serverSalt;
private:
void SendHandshake();
void SendControllerSelection(uint8_t port);
void ClearInputData();
void PushControllerState(uint8_t port, ControlDeviceState state);
void DisableControllers();
bool AttemptLoadGame(string filename, string sha1Hash);
protected:
void ProcessMessage(NetMessage* message) override;
public:
GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData &connectionData);
virtual ~GameClientConnection();
void Shutdown();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
bool SetInput(BaseControlDevice *device) override;
void InitControlDevice();
void SendInput();
void SelectController(uint8_t port);
uint8_t GetAvailableControllers();
uint8_t GetControllerPort();
};

99
Core/GameConnection.cpp Normal file
View file

@ -0,0 +1,99 @@
#include "stdafx.h"
#include "GameConnection.h"
#include "HandShakeMessage.h"
#include "InputDataMessage.h"
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "PlayerListMessage.h"
#include "SelectControllerMessage.h"
#include "ClientConnectionData.h"
#include "ForceDisconnectMessage.h"
#include "ServerInformationMessage.h"
GameConnection::GameConnection(shared_ptr<Console> console, shared_ptr<Socket> socket)
{
_console = console;
_socket = socket;
}
void GameConnection::ReadSocket()
{
auto lock = _socketLock.AcquireSafe();
int bytesReceived = _socket->Recv((char*)_readBuffer + _readPosition, GameConnection::MaxMsgLength - _readPosition, 0);
if(bytesReceived > 0) {
_readPosition += bytesReceived;
}
}
bool GameConnection::ExtractMessage(void *buffer, uint32_t &messageLength)
{
messageLength = _readBuffer[0] | (_readBuffer[1] << 8) | (_readBuffer[2] << 16) | (_readBuffer[3] << 24);
if(messageLength > GameConnection::MaxMsgLength) {
MessageManager::Log("[Netplay] Invalid data received, closing connection.");
Disconnect();
return false;
}
int packetLength = messageLength + sizeof(messageLength);
if(_readPosition >= packetLength) {
memcpy(buffer, _readBuffer+sizeof(messageLength), messageLength);
memmove(_readBuffer, _readBuffer + packetLength, _readPosition - packetLength);
_readPosition -= packetLength;
return true;
}
return false;
}
NetMessage* GameConnection::ReadMessage()
{
ReadSocket();
if(_readPosition > 4) {
uint32_t messageLength;
if(ExtractMessage(_messageBuffer, messageLength)) {
switch((MessageType)_messageBuffer[0]) {
case MessageType::HandShake: return new HandShakeMessage(_messageBuffer, messageLength);
case MessageType::SaveState: return new SaveStateMessage(_messageBuffer, messageLength);
case MessageType::InputData: return new InputDataMessage(_messageBuffer, messageLength);
case MessageType::MovieData: return new MovieDataMessage(_messageBuffer, messageLength);
case MessageType::GameInformation: return new GameInformationMessage(_messageBuffer, messageLength);
case MessageType::PlayerList: return new PlayerListMessage(_messageBuffer, messageLength);
case MessageType::SelectController: return new SelectControllerMessage(_messageBuffer, messageLength);
case MessageType::ForceDisconnect: return new ForceDisconnectMessage(_messageBuffer, messageLength);
case MessageType::ServerInformation: return new ServerInformationMessage(_messageBuffer, messageLength);
}
}
}
return nullptr;
}
void GameConnection::SendNetMessage(NetMessage &message)
{
auto lock = _socketLock.AcquireSafe();
message.Send(*_socket.get());
}
void GameConnection::Disconnect()
{
auto lock = _socketLock.AcquireSafe();
_socket->Close();
}
bool GameConnection::ConnectionError()
{
return _socket->ConnectionError();
}
void GameConnection::ProcessMessages()
{
NetMessage* message;
while((message = ReadMessage()) != nullptr) {
//Loop until all messages have been processed
message->Initialize();
ProcessMessage(message);
delete message;
}
}

48
Core/GameConnection.h Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
class Socket;
class NetMessage;
class Console;
struct PlayerInfo
{
string Name;
uint8_t ControllerPort;
bool IsHost;
};
class GameConnection
{
protected:
static constexpr int MaxMsgLength = 1500000;
shared_ptr<Socket> _socket;
shared_ptr<Console> _console;
uint8_t _readBuffer[GameConnection::MaxMsgLength] = {};
uint8_t _messageBuffer[GameConnection::MaxMsgLength] = {};
int _readPosition = 0;
SimpleLock _socketLock;
private:
void ReadSocket();
bool ExtractMessage(void *buffer, uint32_t &messageLength);
NetMessage* ReadMessage();
virtual void ProcessMessage(NetMessage* message) = 0;
protected:
void Disconnect();
public:
static constexpr uint8_t SpectatorPort = 0xFF;
GameConnection(shared_ptr<Console> console, shared_ptr<Socket> socket);
bool ConnectionError();
void ProcessMessages();
void SendNetMessage(NetMessage &message);
};

View file

@ -0,0 +1,55 @@
#pragma once
#include "stdafx.h"
#include "MessageManager.h"
#include "NetMessage.h"
#include "../Utilities/FolderUtilities.h"
class GameInformationMessage : public NetMessage
{
private:
char* _romFilename = nullptr;
uint32_t _romFilenameLength = 0;
char _sha1Hash[40];
uint8_t _controllerPort = 0;
bool _paused = false;
protected:
virtual void ProtectedStreamState()
{
StreamArray((void**)&_romFilename, _romFilenameLength);
StreamArray((void**)&_sha1Hash, 40);
Stream<uint8_t>(_controllerPort);
Stream<bool>(_paused);
}
public:
GameInformationMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
GameInformationMessage(string filepath, string sha1Hash, uint8_t port, bool paused) : NetMessage(MessageType::GameInformation)
{
CopyString(&_romFilename, _romFilenameLength, FolderUtilities::GetFilename(filepath, true));
memcpy(_sha1Hash, sha1Hash.c_str(), 40);
_controllerPort = port;
_paused = paused;
}
uint8_t GetPort()
{
return _controllerPort;
}
string GetRomFilename()
{
return _romFilename;
}
string GetSha1Hash()
{
return string(_sha1Hash, _sha1Hash+40);
}
bool IsPaused()
{
return _paused;
}
};

258
Core/GameServer.cpp Normal file
View file

@ -0,0 +1,258 @@
#include "stdafx.h"
#include <thread>
using std::thread;
#include "MessageManager.h"
#include "GameServer.h"
#include "Console.h"
#include "ControlManager.h"
#include "Multitap.h"
#include "PlayerListMessage.h"
#include "NotificationManager.h"
#include "../Utilities/Socket.h"
shared_ptr<GameServer> GameServer::Instance;
GameServer::GameServer(shared_ptr<Console> console, uint16_t listenPort, string password, string hostPlayerName)
{
_console = console;
_stop = false;
_port = listenPort;
_password = password;
_hostPlayerName = hostPlayerName;
_hostControllerPort = 0;
//If a game is already running, register ourselves as an input recorder/provider right away
RegisterServerInput();
}
GameServer::~GameServer()
{
_stop = true;
_serverThread->join();
Stop();
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
if(controlManager) {
controlManager->UnregisterInputRecorder(this);
controlManager->UnregisterInputProvider(this);
}
}
void GameServer::RegisterServerInput()
{
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
if(controlManager) {
controlManager->RegisterInputRecorder(this);
controlManager->RegisterInputProvider(this);
}
}
void GameServer::AcceptConnections()
{
while(true) {
shared_ptr<Socket> socket = _listener->Accept();
if(!socket->ConnectionError()) {
auto connection = shared_ptr<GameServerConnection>(new GameServerConnection(_console, socket, _password));
_console->GetNotificationManager()->RegisterNotificationListener(connection);
_openConnections.push_back(connection);
} else {
break;
}
}
_listener->Listen(10);
}
void GameServer::UpdateConnections()
{
vector<shared_ptr<GameServerConnection>> connectionsToRemove;
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(connection->ConnectionError()) {
connectionsToRemove.push_back(connection);
} else {
connection->ProcessMessages();
}
}
for(shared_ptr<GameServerConnection> gameConnection : connectionsToRemove) {
_openConnections.remove(gameConnection);
}
}
list<shared_ptr<GameServerConnection>> GameServer::GetConnectionList()
{
if(GameServer::Started()) {
return Instance->_openConnections;
} else {
return list<shared_ptr<GameServerConnection>>();
}
}
bool GameServer::SetInput(BaseControlDevice *device)
{
uint8_t port = device->GetPort();
if(device->GetControllerType() == ControllerType::Multitap) {
//Need special handling for the multitap, merge data from P3/4/5 with P1 (or P2, depending which port the multitap is plugged into)
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
if(connection) {
((Multitap*)device)->SetControllerState(0, connection->GetState());
}
for(int i = 2; i < 5; i++) {
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(i);
if(connection) {
((Multitap*)device)->SetControllerState(i - 1, connection->GetState());
}
}
} else {
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
if(connection) {
//Device is controlled by a client
device->SetRawState(connection->GetState());
return true;
}
}
//Host is controlling this device
return false;
}
void GameServer::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
for(shared_ptr<BaseControlDevice> &device : devices) {
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(!connection->ConnectionError()) {
//Send movie stream
connection->SendMovieData(device->GetPort(), device->GetRawState());
}
}
}
}
void GameServer::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
//Register the server as an input provider/recorder
RegisterServerInput();
}
}
void GameServer::Exec()
{
_listener.reset(new Socket());
_listener->Bind(_port);
_listener->Listen(10);
_stop = false;
_initialized = true;
MessageManager::DisplayMessage("NetPlay" , "ServerStarted", std::to_string(_port));
while(!_stop) {
AcceptConnections();
UpdateConnections();
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
}
}
void GameServer::Stop()
{
_initialized = false;
_listener.reset();
MessageManager::DisplayMessage("NetPlay", "ServerStopped");
}
void GameServer::StartServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName)
{
Instance.reset(new GameServer(console, port, password, hostPlayerName));
console->GetNotificationManager()->RegisterNotificationListener(Instance);
Instance->_serverThread.reset(new thread(&GameServer::Exec, Instance.get()));
}
void GameServer::StopServer()
{
if(Instance) {
Instance.reset();
}
}
bool GameServer::Started()
{
if(Instance) {
return Instance->_initialized;
} else {
return false;
}
}
string GameServer::GetHostPlayerName()
{
if(GameServer::Started()) {
return Instance->_hostPlayerName;
}
return "";
}
uint8_t GameServer::GetHostControllerPort()
{
if(GameServer::Started()) {
return Instance->_hostControllerPort;
}
return GameConnection::SpectatorPort;
}
void GameServer::SetHostControllerPort(uint8_t port)
{
if(GameServer::Started()) {
Instance->_console->Lock();
if(port == GameConnection::SpectatorPort || GetAvailableControllers() & (1 << port)) {
//Port is available
Instance->_hostControllerPort = port;
SendPlayerList();
}
Instance->_console->Unlock();
}
}
uint8_t GameServer::GetAvailableControllers()
{
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
for(PlayerInfo &playerInfo : GetPlayerList()) {
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
availablePorts &= ~(1 << playerInfo.ControllerPort);
}
}
return availablePorts;
}
vector<PlayerInfo> GameServer::GetPlayerList()
{
vector<PlayerInfo> playerList;
PlayerInfo playerInfo;
playerInfo.Name = GetHostPlayerName();
playerInfo.ControllerPort = GetHostControllerPort();
playerInfo.IsHost = true;
playerList.push_back(playerInfo);
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
playerInfo.Name = connection->GetPlayerName();
playerInfo.ControllerPort = connection->GetControllerPort();
playerInfo.IsHost = false;
playerList.push_back(playerInfo);
}
return playerList;
}
void GameServer::SendPlayerList()
{
vector<PlayerInfo> playerList = GetPlayerList();
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
//Send player list update to all connections
PlayerListMessage message(playerList);
connection->SendNetMessage(message);
}
}

58
Core/GameServer.h Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "GameServerConnection.h"
#include "INotificationListener.h"
#include "IInputProvider.h"
#include "IInputRecorder.h"
using std::thread;
class Console;
class GameServer : public IInputRecorder, public IInputProvider, public INotificationListener
{
private:
static shared_ptr<GameServer> Instance;
shared_ptr<Console> _console;
unique_ptr<thread> _serverThread;
atomic<bool> _stop;
unique_ptr<Socket> _listener;
uint16_t _port;
string _password;
list<shared_ptr<GameServerConnection>> _openConnections;
bool _initialized = false;
string _hostPlayerName;
uint8_t _hostControllerPort;
void AcceptConnections();
void UpdateConnections();
void Exec();
void Stop();
public:
GameServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName);
virtual ~GameServer();
void RegisterServerInput();
static void StartServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName);
static void StopServer();
static bool Started();
static string GetHostPlayerName();
static uint8_t GetHostControllerPort();
static void SetHostControllerPort(uint8_t port);
static uint8_t GetAvailableControllers();
static vector<PlayerInfo> GetPlayerList();
static void SendPlayerList();
static list<shared_ptr<GameServerConnection>> GetConnectionList();
bool SetInput(BaseControlDevice *device) override;
void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
};

View file

@ -0,0 +1,256 @@
#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;
}

View file

@ -0,0 +1,52 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "GameConnection.h"
#include "INotificationListener.h"
#include "BaseControlDevice.h"
#include "ControlDeviceState.h"
class HandShakeMessage;
class GameServerConnection : public GameConnection, public INotificationListener
{
private:
static GameServerConnection* _netPlayDevices[BaseControlDevice::PortCount];
list<ControlDeviceState> _inputData;
string _playerName;
int _controllerPort;
string _connectionHash;
string _serverPassword;
bool _handshakeCompleted = false;
void PushState(ControlDeviceState state);
void SendServerInformation();
void SendGameInformation();
void SelectControllerPort(uint8_t port);
void SendForceDisconnectMessage(string disconnectMessage);
void ProcessHandshakeResponse(HandShakeMessage* message);
static void RegisterNetPlayDevice(GameServerConnection* connection, uint8_t port);
static void UnregisterNetPlayDevice(GameServerConnection* device);
static uint8_t GetFirstFreeControllerPort();
protected:
void ProcessMessage(NetMessage* message) override;
public:
GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, string serverPassword);
virtual ~GameServerConnection();
ControlDeviceState GetState();
void SendMovieData(uint8_t port, ControlDeviceState state);
string GetPlayerName();
uint8_t GetControllerPort();
virtual void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
static GameServerConnection* GetNetPlayDevice(uint8_t port);
};

66
Core/HandShakeMessage.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
#include "../Utilities/sha1.h"
class HandShakeMessage : public NetMessage
{
private:
static constexpr int CurrentVersion = 100; //Use 100+ to distinguish from Mesen
uint32_t _emuVersion = 0;
uint32_t _protocolVersion = CurrentVersion;
char* _playerName = nullptr;
uint32_t _playerNameLength = 0;
char* _hashedPassword = nullptr;
uint32_t _hashedPasswordLength = 0;
bool _spectator = false;
protected:
virtual void ProtectedStreamState()
{
Stream<uint32_t>(_emuVersion);
Stream<uint32_t>(_protocolVersion);
StreamArray((void**)&_playerName, _playerNameLength);
StreamArray((void**)&_hashedPassword, _hashedPasswordLength);
Stream<bool>(_spectator);
}
public:
HandShakeMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) {}
HandShakeMessage(string playerName, string hashedPassword, bool spectator, uint32_t emuVersion) : NetMessage(MessageType::HandShake)
{
_emuVersion = emuVersion;
_protocolVersion = HandShakeMessage::CurrentVersion;
CopyString(&_playerName, _playerNameLength, playerName);
CopyString(&_hashedPassword, _hashedPasswordLength, hashedPassword);
_spectator = spectator;
}
string GetPlayerName()
{
return string(_playerName);
}
bool IsValid(uint32_t emuVersion)
{
return _protocolVersion == CurrentVersion && _emuVersion == emuVersion;
}
bool CheckPassword(string serverPassword, string connectionHash)
{
return GetPasswordHash(serverPassword, connectionHash) == string(_hashedPassword);
}
bool IsSpectator()
{
return _spectator;
}
static string GetPasswordHash(string serverPassword, string connectionHash)
{
string saltedPassword = serverPassword + connectionHash;
vector<uint8_t> dataToHash = vector<uint8_t>(saltedPassword.c_str(), saltedPassword.c_str() + saltedPassword.size());
return SHA1::GetHash(dataToHash);
}
};

View file

@ -20,6 +20,7 @@ enum class ConsoleNotificationType
EventViewerRefresh = 14,
MissingFirmware = 15,
BeforeGameUnload = 16,
CheatsChanged = 17
};
class INotificationListener

29
Core/InputDataMessage.h Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
#include "ControlDeviceState.h"
class InputDataMessage : public NetMessage
{
private:
ControlDeviceState _inputState;
protected:
virtual void ProtectedStreamState()
{
StreamArray(_inputState.State);
}
public:
InputDataMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
InputDataMessage(ControlDeviceState inputState) : NetMessage(MessageType::InputData)
{
_inputState = inputState;
}
ControlDeviceState GetInputState()
{
return _inputState;
}
};

View file

@ -254,6 +254,7 @@ void MesenMovie::ApplySettings()
}
emuConfig.PpuExtraScanlinesAfterNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi);
emuConfig.PpuExtraScanlinesBeforeNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi);
emuConfig.GsuClockSpeed = LoadInt(_settings, MovieKeys::GsuClockSpeed, 100);
settings->SetEmulationConfig(emuConfig);
settings->SetInputConfig(inputConfig);

View file

@ -32,7 +32,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
{ "ConnectionLost", u8"Connection to server lost." },
{ "CouldNotConnect", u8"Could not connect to the server." },
{ "CouldNotInitializeAudioSystem", u8"Could not initialize audio system" },
{ "CouldNotFindRom", u8"Could not find matching game ROM." },
{ "CouldNotFindRom", u8"Could not find matching game ROM. (%1)" },
{ "CouldNotWriteToFile", u8"Could not write to file: %1" },
{ "CouldNotLoadFile", u8"Could not load file: %1" },
{ "EmulationMaximumSpeed", u8"Maximum speed" },
@ -53,6 +53,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
{ "MovieRecordingTo", u8"Recording to: %1" },
{ "MovieSaved", u8"Movie saved to file: %1" },
{ "NetplayVersionMismatch", u8"%1 is not running the same version of Mesen and has been disconnected." },
{ "NetplayNotAllowed", u8"This action is not allowed while connected to a server." },
{ "OverclockEnabled", u8"Overclocking enabled." },
{ "OverclockDisabled", u8"Overclocking disabled." },
{ "PrgSizeWarning", u8"PRG size is smaller than 32kb" },

15
Core/MessageType.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include "stdafx.h"
enum class MessageType : uint8_t
{
HandShake = 0,
SaveState = 1,
InputData = 2,
MovieData = 3,
GameInformation = 4,
PlayerList = 5,
SelectController = 6,
ForceDisconnect = 7,
ServerInformation = 8
};

37
Core/MovieDataMessage.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
#include "ControlDeviceState.h"
class MovieDataMessage : public NetMessage
{
private:
uint8_t _portNumber;
ControlDeviceState _inputState;
protected:
virtual void ProtectedStreamState()
{
Stream<uint8_t>(_portNumber);
StreamArray(_inputState.State);
}
public:
MovieDataMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
MovieDataMessage(ControlDeviceState state, uint8_t port) : NetMessage(MessageType::MovieData)
{
_portNumber = port;
_inputState = state;
}
uint8_t GetPortNumber()
{
return _portNumber;
}
ControlDeviceState GetInputState()
{
return _inputState;
}
};

View file

@ -16,6 +16,7 @@
#include "MovieTypes.h"
#include "BatteryManager.h"
#include "CheatManager.h"
#include "BaseCartridge.h"
MovieRecorder::MovieRecorder(shared_ptr<Console> console)
{
@ -79,7 +80,7 @@ void MovieRecorder::GetGameSettings(stringstream &out)
VirtualFile romFile = _console->GetRomInfo().RomFile;
WriteString(out, MovieKeys::GameFile, romFile.GetFileName());
WriteString(out, MovieKeys::Sha1, romFile.GetSha1Hash());
WriteString(out, MovieKeys::Sha1, _console->GetCartridge()->GetSha1Hash());
VirtualFile patchFile = _console->GetRomInfo().PatchFile;
if(patchFile.IsValid()) {
@ -106,6 +107,7 @@ void MovieRecorder::GetGameSettings(stringstream &out)
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, emuConfig.PpuExtraScanlinesBeforeNmi);
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, emuConfig.PpuExtraScanlinesAfterNmi);
WriteInt(out, MovieKeys::GsuClockSpeed, emuConfig.GsuClockSpeed);
switch(emuConfig.RamPowerOnState) {
case RamState::AllZeros: WriteString(out, MovieKeys::RamPowerOnState, "AllZeros"); break;

View file

@ -57,4 +57,5 @@ namespace MovieKeys
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
constexpr const char* RamPowerOnState = "RamPowerOnState";
constexpr const char* InputPollScanline = "InputPollScanline";
constexpr const char* GsuClockSpeed = "GsuClockSpeed";
};

View file

@ -2,6 +2,7 @@
#include "Multitap.h"
#include "InternalRegisters.h"
#include "Ppu.h"
#include "SnesController.h"
string Multitap::GetKeyNames()
{
@ -41,6 +42,23 @@ void Multitap::InternalSetStateFromInput()
}
}
void Multitap::UpdateControllerState(uint8_t controllerNumber, SnesController &controller)
{
int offset = Multitap::ButtonCount * controllerNumber;
SetBitValue(Buttons::A + offset, controller.IsPressed(Buttons::A));
SetBitValue(Buttons::B + offset, controller.IsPressed(Buttons::B));
SetBitValue(Buttons::X + offset, controller.IsPressed(Buttons::X));
SetBitValue(Buttons::Y + offset, controller.IsPressed(Buttons::Y));
SetBitValue(Buttons::L + offset, controller.IsPressed(Buttons::L));
SetBitValue(Buttons::R + offset, controller.IsPressed(Buttons::R));
SetBitValue(Buttons::Start + offset, controller.IsPressed(Buttons::Start));
SetBitValue(Buttons::Select + offset, controller.IsPressed(Buttons::Select));
SetBitValue(Buttons::Up + offset, controller.IsPressed(Buttons::Up));
SetBitValue(Buttons::Down + offset, controller.IsPressed(Buttons::Down));
SetBitValue(Buttons::Left + offset, controller.IsPressed(Buttons::Left));
SetBitValue(Buttons::Right + offset, controller.IsPressed(Buttons::Right));
}
uint16_t Multitap::ToByte(uint8_t port)
{
//"A Super NES controller returns a 16-bit report in a similar order: B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, then four 0 bits."
@ -95,6 +113,13 @@ ControllerType Multitap::GetControllerType()
return ControllerType::Multitap;
}
void Multitap::SetControllerState(uint8_t controllerNumber, ControlDeviceState state)
{
SnesController controller(_console, 0, KeyMappingSet());
controller.SetRawState(state);
UpdateControllerState(controllerNumber, controller);
}
uint8_t Multitap::ReadRam(uint16_t addr)
{
uint8_t selectBit = 0x80 >> ((_port == 0) ? 1 : 0);

View file

@ -5,6 +5,7 @@
class Ppu;
class InternalRegisters;
class SnesController;
class Multitap : public BaseControlDevice
{
@ -21,6 +22,7 @@ private:
protected:
string GetKeyNames() override;
void InternalSetStateFromInput() override;
void UpdateControllerState(uint8_t controllerNumber, SnesController &controller);
uint16_t ToByte(uint8_t port);
void Serialize(Serializer &s) override;
void RefreshStateBuffer() override;
@ -30,6 +32,8 @@ public:
ControllerType GetControllerType() override;
void SetControllerState(uint8_t controllerNumber, ControlDeviceState state);
uint8_t ReadRam(uint16_t addr) override;
void WriteRam(uint16_t addr, uint8_t value) override;
};

139
Core/NetMessage.h Normal file
View file

@ -0,0 +1,139 @@
#pragma once
#include "stdafx.h"
#include "MessageType.h"
#include "../Utilities/Socket.h"
class NetMessage
{
protected:
MessageType _type;
bool _sending;
vector<uint8_t> _buffer;
uint32_t _position = 0;
vector<uint8_t*> _arraysToRelease;
vector<char*> _stringsToRelease;
template<typename T>
void Stream(T &value)
{
if(_sending) {
uint8_t* bytes = (uint8_t*)&value;
int typeSize = sizeof(T);
for(int i = 0; i < typeSize; i++) {
_buffer.push_back(bytes[i]);
}
} else {
memcpy(&value, _buffer.data()+_position, sizeof(T));
_position += sizeof(T);
}
}
void StreamArray(void* value, uint32_t length)
{
void* pointer = value;
uint32_t len = length;
StreamArray(&pointer, len);
}
void StreamArray(void** value, uint32_t &length)
{
if(_sending) {
Stream<uint32_t>(length);
uint8_t* bytes = (uint8_t*)(*value);
for(uint32_t i = 0, len = length; i < len; i++) {
_buffer.push_back(bytes[i]);
_position++;
}
} else {
Stream<uint32_t>(length);
if(*value == nullptr) {
*value = (void*)new uint8_t[length];
_arraysToRelease.push_back((uint8_t*)*value);
}
uint8_t* bytes = (uint8_t*)(*value);
for(uint32_t i = 0, len = length; i < len; i++) {
bytes[i] = _buffer[_position];
_position++;
}
}
}
void StreamArray(vector<uint8_t> &data)
{
uint32_t length = (uint32_t)data.size();
Stream<uint32_t>(length);
if(_sending) {
uint8_t* bytes = (uint8_t*)data.data();
for(uint32_t i = 0, len = length; i < len; i++) {
_buffer.push_back(bytes[i]);
_position++;
}
} else {
data.resize(length, 0);
uint8_t* bytes = (uint8_t*)data.data();
for(uint32_t i = 0, len = length; i < len; i++) {
bytes[i] = _buffer[_position];
_position++;
}
}
}
void StreamState()
{
Stream<MessageType>(_type);
ProtectedStreamState();
}
NetMessage(MessageType type)
{
_type = type;
_sending = true;
}
NetMessage(void* buffer, uint32_t length)
{
_buffer.assign((uint8_t*)buffer, (uint8_t*)buffer + length);
_sending = false;
}
public:
virtual ~NetMessage()
{
for(uint8_t *arrayPtr: _arraysToRelease) {
delete[] arrayPtr;
}
for(char *stringPtr: _stringsToRelease) {
delete[] stringPtr;
}
}
void Initialize()
{
StreamState();
}
MessageType GetType()
{
return _type;
}
void Send(Socket &socket)
{
StreamState();
uint32_t messageLength = (uint32_t)_buffer.size();
_buffer.insert(_buffer.begin(), (char*)&messageLength, (char*)&messageLength + sizeof(messageLength));
socket.Send((char*)_buffer.data(), (int)_buffer.size(), 0);
}
void CopyString(char** dest, uint32_t &length, string src)
{
length = (uint32_t)(src.length() + 1);
*dest = new char[length];
memcpy(*dest, src.c_str(), length);
_stringsToRelease.push_back(*dest);
}
protected:
virtual void ProtectedStreamState() = 0;
};

64
Core/PlayerListMessage.h Normal file
View file

@ -0,0 +1,64 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
class PlayerListMessage : public NetMessage
{
private:
vector<PlayerInfo> _playerList;
protected:
virtual void ProtectedStreamState()
{
constexpr uint32_t PlayerNameMaxLength = 50;
uint32_t nameLength = PlayerNameMaxLength + 1;
char playerName[PlayerNameMaxLength + 1];
uint8_t playerPort = 0;
bool isHost = false;
if(_sending) {
uint32_t playerCount = (uint32_t)_playerList.size();
Stream<uint32_t>(playerCount);
for(uint32_t i = 0; i < playerCount; i++) {
memset(playerName, 0, nameLength);
memcpy(playerName, _playerList[i].Name.c_str(), std::min((uint32_t)_playerList[i].Name.size(), PlayerNameMaxLength));
playerPort = _playerList[i].ControllerPort;
StreamArray(playerName, nameLength);
Stream<uint8_t>(playerPort);
Stream<bool>(isHost);
}
} else {
uint32_t playerCount;
Stream<uint32_t>(playerCount);
for(uint32_t i = 0; i < playerCount; i++) {
memset(playerName, 0, nameLength);
StreamArray(playerName, nameLength);
Stream<uint8_t>(playerPort);
Stream<bool>(isHost);
PlayerInfo playerInfo;
playerInfo.Name = playerName;
playerInfo.ControllerPort = playerPort;
playerInfo.IsHost = isHost;
_playerList.push_back(playerInfo);
}
}
}
public:
PlayerListMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
PlayerListMessage(vector<PlayerInfo> playerList) : NetMessage(MessageType::PlayerList)
{
_playerList = playerList;
}
vector<PlayerInfo> GetPlayerList()
{
return _playerList;
}
};

49
Core/RomFinder.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include "stdafx.h"
#include "Console.h"
#include "BaseCartridge.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
class RomFinder
{
public:
static bool LoadMatchingRom(Console* console, string romName, string sha1Hash)
{
if(console->IsRunning() && console->GetCartridge()->GetSha1Hash() == sha1Hash) {
//Current game matches
return true;
}
string match = FindMatchingRom(console, romName, sha1Hash);
if(!match.empty()) {
return console->LoadRom(match, VirtualFile(""));
}
return false;
}
static string FindMatchingRom(Console* console, string romName, string sha1Hash)
{
if(console->IsRunning() && console->GetCartridge()->GetSha1Hash() == sha1Hash) {
//Current game matches
return console->GetRomInfo().RomFile;
}
string lcRomname = romName;
std::transform(lcRomname.begin(), lcRomname.end(), lcRomname.begin(), ::tolower);
std::transform(romName.begin(), romName.end(), romName.begin(), ::tolower);
vector<string> romFiles;
for(string folder : FolderUtilities::GetKnownGameFolders()) {
for(string romFilename : FolderUtilities::GetFilesInFolder(folder, VirtualFile::RomExtensions, true)) {
string lcRomFile = romFilename;
std::transform(lcRomFile.begin(), lcRomFile.end(), lcRomFile.begin(), ::tolower);
if(FolderUtilities::GetFilename(romName, false) == FolderUtilities::GetFilename(lcRomFile, false) && VirtualFile(romFilename).GetSha1Hash() == sha1Hash) {
return romFilename;
}
}
}
return "";
}
};

View file

@ -11,6 +11,7 @@
#include "MovieManager.h"
#include "EventType.h"
#include "Debugger.h"
#include "GameClient.h"
SaveStateManager::SaveStateManager(shared_ptr<Console> console)
{
@ -115,6 +116,11 @@ void SaveStateManager::SaveState(int stateIndex, bool displayMessage)
bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
{
if(GameClient::Connected()) {
MessageManager::DisplayMessage("Netplay", "NetplayNotAllowed");
return false;
}
char header[3];
stream.read(header, 3);
if(memcmp(header, "MSS", 3) == 0) {

102
Core/SaveStateMessage.h Normal file
View file

@ -0,0 +1,102 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
#include "Console.h"
#include "EmuSettings.h"
#include "CheatManager.h"
#include "SaveStateManager.h"
class SaveStateMessage : public NetMessage
{
private:
vector<CheatCode> _activeCheats;
uint8_t* _stateData = nullptr;
uint32_t _dataSize = 0;
ControllerType _controllerTypes[5];
ConsoleRegion _region;
uint32_t _ppuExtraScanlinesAfterNmi;
uint32_t _ppuExtraScanlinesBeforeNmi;
uint32_t _gsuClockSpeed;
CheatCode* _cheats = nullptr;
uint32_t _cheatArraySize = 0;
protected:
virtual void ProtectedStreamState()
{
StreamArray((void**)&_stateData, _dataSize);
Stream(_region);
Stream(_ppuExtraScanlinesAfterNmi);
Stream(_ppuExtraScanlinesBeforeNmi);
Stream(_gsuClockSpeed);
StreamArray(_controllerTypes, sizeof(ControllerType) * 5);
if(_sending) {
_cheats = _activeCheats.size() > 0 ? &_activeCheats[0] : nullptr;
_cheatArraySize = (uint32_t)_activeCheats.size() * sizeof(CheatCode);
StreamArray((void**)&_cheats, _cheatArraySize);
delete[] _stateData;
} else {
StreamArray((void**)&_cheats, _cheatArraySize);
}
}
public:
SaveStateMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
SaveStateMessage(shared_ptr<Console> console) : NetMessage(MessageType::SaveState)
{
//Used when sending state to clients
console->Lock();
_activeCheats = console->GetCheatManager()->GetCheats();
stringstream state;
console->Serialize(state);
EmulationConfig emuCfg = console->GetSettings()->GetEmulationConfig();
_region = emuCfg.Region;
_ppuExtraScanlinesAfterNmi = emuCfg.PpuExtraScanlinesAfterNmi;
_ppuExtraScanlinesBeforeNmi = emuCfg.PpuExtraScanlinesBeforeNmi;
_gsuClockSpeed = emuCfg.GsuClockSpeed;
InputConfig inputCfg = console->GetSettings()->GetInputConfig();
for(int i = 0; i < 5; i++) {
_controllerTypes[i] = inputCfg.Controllers[i].Type;
}
console->Unlock();
_dataSize = (uint32_t)state.tellp();
_stateData = new uint8_t[_dataSize];
state.read((char*)_stateData, _dataSize);
}
void LoadState(shared_ptr<Console> console)
{
std::stringstream ss;
ss.write((char*)_stateData, _dataSize);
console->Deserialize(ss, SaveStateManager::FileFormatVersion);
vector<CheatCode> cheats;
for(uint32_t i = 0; i < _cheatArraySize / sizeof(CheatCode); i++) {
cheats.push_back(((CheatCode*)_cheats)[i]);
}
console->GetCheatManager()->SetCheats(cheats);
EmulationConfig emuCfg = console->GetSettings()->GetEmulationConfig();
emuCfg.Region = _region;
emuCfg.PpuExtraScanlinesAfterNmi = _ppuExtraScanlinesAfterNmi;
emuCfg.PpuExtraScanlinesBeforeNmi = _ppuExtraScanlinesBeforeNmi;
emuCfg.GsuClockSpeed = _gsuClockSpeed;
InputConfig inputCfg = console->GetSettings()->GetInputConfig();
for(int i = 0; i < 5; i++) {
inputCfg.Controllers[i].Type = _controllerTypes[i];
}
console->GetSettings()->SetEmulationConfig(emuCfg);
console->GetSettings()->SetInputConfig(inputCfg);
}
};

View file

@ -0,0 +1,28 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
class SelectControllerMessage : public NetMessage
{
private:
uint8_t _portNumber;
protected:
virtual void ProtectedStreamState()
{
Stream<uint8_t>(_portNumber);
}
public:
SelectControllerMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
SelectControllerMessage(uint8_t port) : NetMessage(MessageType::SelectController)
{
_portNumber = port;
}
uint8_t GetPortNumber()
{
return _portNumber;
}
};

View file

@ -0,0 +1,28 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
class ServerInformationMessage : public NetMessage
{
private:
char* _hashSalt = nullptr;
uint32_t _hashSaltLength = 0;
protected:
virtual void ProtectedStreamState()
{
StreamArray((void**)&_hashSalt, _hashSaltLength);
}
public:
ServerInformationMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) {}
ServerInformationMessage(string hashSalt) : NetMessage(MessageType::ServerInformation)
{
CopyString(&_hashSalt, _hashSaltLength, hashSalt);
}
string GetHashSalt()
{
return string(_hashSalt);
}
};

View file

@ -9,6 +9,7 @@
#include "NotificationManager.h"
#include "SaveStateManager.h"
#include "MovieManager.h"
#include "GameClient.h"
ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr<Console> console)
{
@ -102,8 +103,8 @@ void ShortcutKeyHandler::ProcessRunSingleFrame()
void ShortcutKeyHandler::CheckMappedKeys()
{
shared_ptr<EmuSettings> settings = _console->GetSettings();
bool isNetplayClient = false; //TODO GameClient::Connected();
//bool isMovieActive = MovieManager::Playing() || MovieManager::Recording();
bool isNetplayClient = GameClient::Connected();
bool isMovieActive = _console->GetMovieManager()->Playing() || _console->GetMovieManager()->Recording();
bool isMovieRecording = _console->GetMovieManager()->Recording();
//Let the UI handle these shortcuts
@ -140,13 +141,13 @@ void ShortcutKeyHandler::CheckMappedKeys()
_console->GetSaveStateManager()->SaveState();
}
if(DetectKeyPress(EmulatorShortcut::LoadState) && !isNetplayClient) {
if(DetectKeyPress(EmulatorShortcut::LoadState)) {
_console->GetSaveStateManager()->LoadState();
}
/*if(DetectKeyPress(EmulatorShortcut::ToggleCheats) && !isNetplayClient && !isMovieActive) {
if(DetectKeyPress(EmulatorShortcut::ToggleCheats) && !isNetplayClient && !isMovieActive) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleCheats);
}*/
}
if(DetectKeyPress(EmulatorShortcut::ToggleAudio)) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleAudio);

View file

@ -40,6 +40,11 @@ extern "C" {
_console->GetSettings()->SetShortcutKeys(shortcutList);
}
DllExport ControllerType __stdcall GetControllerType(int player)
{
return _console->GetSettings()->GetInputConfig().Controllers[player].Type;
}
DllExport const char* __stdcall GetAudioDevices()
{
_returnString = _soundManager ? _soundManager->GetAvailableDevices() : "";

View file

@ -11,6 +11,8 @@
#include "../Core/KeyManager.h"
#include "../Core/ShortcutKeyHandler.h"
#include "../Core/CheatManager.h"
#include "../Core/GameClient.h"
#include "../Core/GameServer.h"
#include "../Utilities/ArchiveReader.h"
#include "../Utilities/FolderUtilities.h"
#include "InteropNotificationListeners.h"
@ -109,8 +111,13 @@ extern "C" {
}
}
DllExport bool __stdcall LoadRom(char* filename, char* patchFile) { return _console->LoadRom((VirtualFile)filename, patchFile ? (VirtualFile)patchFile : VirtualFile()); }
//DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); }
DllExport bool __stdcall LoadRom(char* filename, char* patchFile)
{
GameClient::Disconnect();
return _console->LoadRom((VirtualFile)filename, patchFile ? (VirtualFile)patchFile : VirtualFile());
}
DllExport void __stdcall AddKnownGameFolder(char* folder) { FolderUtilities::AddKnownGameFolder(folder); }
DllExport void __stdcall GetRomInfo(InteropRomInfo &info)
{
@ -153,17 +160,22 @@ extern "C" {
DllExport void __stdcall Stop()
{
GameClient::Disconnect();
_console->Stop(true);
}
DllExport void __stdcall Pause()
{
_console->Pause();
if(!GameClient::Connected()) {
_console->Pause();
}
}
DllExport void __stdcall Resume()
{
_console->Resume();
if(!GameClient::Connected()) {
_console->Resume();
}
}
DllExport bool __stdcall IsPaused()
@ -177,16 +189,23 @@ extern "C" {
DllExport void __stdcall Reset()
{
_console->GetControlManager()->GetSystemActionManager()->Reset();
if(!GameClient::Connected()) {
_console->GetControlManager()->GetSystemActionManager()->Reset();
}
}
DllExport void __stdcall PowerCycle()
{
_console->GetControlManager()->GetSystemActionManager()->PowerCycle();
if(!GameClient::Connected()) {
_console->GetControlManager()->GetSystemActionManager()->PowerCycle();
}
}
DllExport void __stdcall Release()
{
GameClient::Disconnect();
GameServer::StopServer();
_shortcutKeyHandler.reset();
_console->Stop(true);

View file

@ -457,6 +457,7 @@
<ClCompile Include="EmuApiWrapper.cpp" />
<ClCompile Include="DebugApiWrapper.cpp" />
<ClCompile Include="InputApiWrapper.cpp" />
<ClCompile Include="NetplayApiWrapper.cpp" />
<ClCompile Include="RecordApiWrapper.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

View file

@ -43,5 +43,8 @@
<ClCompile Include="TestApiWrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NetplayApiWrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,50 @@
#include "stdafx.h"
#include "../Core/Console.h"
#include "../Core/ClientConnectionData.h"
#include "../Core/GameServer.h"
#include "../Core/GameClient.h"
#include "../Core/EmuSettings.h"
extern shared_ptr<Console> _console;
extern "C" {
DllExport void __stdcall StartServer(uint16_t port, char* password, char* hostPlayerName) { GameServer::StartServer(_console, port, password, hostPlayerName); }
DllExport void __stdcall StopServer() { GameServer::StopServer(); }
DllExport bool __stdcall IsServerRunning() { return GameServer::Started(); }
DllExport void __stdcall Connect(char* host, uint16_t port, char* password, char* playerName, bool spectator)
{
ClientConnectionData connectionData(host, port, password, playerName, spectator);
GameClient::Connect(_console, connectionData);
}
DllExport void __stdcall Disconnect() { GameClient::Disconnect(); }
DllExport bool __stdcall IsConnected() { return GameClient::Connected(); }
DllExport int32_t __stdcall NetPlayGetAvailableControllers()
{
if(GameServer::Started()) {
return GameServer::GetAvailableControllers();
} else {
return GameClient::GetAvailableControllers();
}
}
DllExport void __stdcall NetPlaySelectController(int32_t port)
{
if(GameServer::Started()) {
return GameServer::SetHostControllerPort(port);
} else {
return GameClient::SelectController(port);
}
}
DllExport int32_t __stdcall NetPlayGetControllerPort()
{
if(GameServer::Started()) {
return GameServer::GetHostControllerPort();
} else {
return GameClient::GetControllerPort();
}
}
}

View file

@ -52,6 +52,11 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/EmuSettings.cpp \
$(CORE_DIR)/EventManager.cpp \
$(CORE_DIR)/ExpressionEvaluator.cpp \
$(CORE_DIR)/GameClient.cpp \
$(CORE_DIR)/GameClientConnection.cpp \
$(CORE_DIR)/GameConnection.cpp \
$(CORE_DIR)/GameServer.cpp \
$(CORE_DIR)/GameServerConnection.cpp \
$(CORE_DIR)/Gsu.cpp \
$(CORE_DIR)/Gsu.Instructions.cpp \
$(CORE_DIR)/GsuDisUtils.cpp \

View file

@ -19,9 +19,10 @@ Linux: [![Build status](https://ci.appveyor.com/api/projects/status/arkaatgy94f2
The following should be added over time (in no particular order):
* Netplay
* Additions/improvements in the debugging tools
* Support for the SPC7110 chip
* SPC7110 support
* MSU-1 support
* Satellaview/BS-X support
## Compiling

View file

@ -25,6 +25,7 @@ namespace Mesen.GUI.Config
public AviRecordConfig AviRecord;
public MovieRecordConfig MovieRecord;
public CheatWindowConfig Cheats;
public NetplayConfig Netplay;
public Point WindowLocation;
public Size WindowSize;
public bool NeedInputReinit = true;
@ -42,6 +43,7 @@ namespace Mesen.GUI.Config
AviRecord = new AviRecordConfig();
MovieRecord = new MovieRecordConfig();
Cheats = new CheatWindowConfig();
Netplay = new NetplayConfig();
}
~Configuration()

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Config
{
public class NetplayConfig : BaseConfig<VideoConfig>
{
public string Host = "localhost";
public UInt16 Port = 8888;
public string Password = "";
public string PlayerName = "PlayerName";
public string ServerName = "Default";
public UInt16 ServerPort = 8888;
public string ServerPassword = "";
}
}

View file

@ -62,9 +62,8 @@
<Control ID="mnuNetPlayPlayer2">Player 2</Control>
<Control ID="mnuNetPlayPlayer3">Player 3</Control>
<Control ID="mnuNetPlayPlayer4">Player 4</Control>
<Control ID="mnuNetPlayPlayer5">Expansion Device</Control>
<Control ID="mnuNetPlayPlayer5">Player 5</Control>
<Control ID="mnuNetPlaySpectator">Spectator</Control>
<Control ID="mnuFindServer">Find Public Server...</Control>
<Control ID="mnuProfile">Configure Profile</Control>
<Control ID="mnuMovies">Movies</Control>
<Control ID="mnuPlayMovie">Play...</Control>
@ -404,10 +403,7 @@
</Form>
<Form ID="frmServerConfig" Title="Server Configuration">
<Control ID="lblPort">Port:</Control>
<Control ID="chkPublicServer">Public server</Control>
<Control ID="lblServerName">Server name:</Control>
<Control ID="chkSpectator">Allow spectators</Control>
<Control ID="lblMaxPlayers">Max. number of players:</Control>
<Control ID="lblPassword">Password:</Control>
<Control ID="btnOK">OK</Control>
<Control ID="btnCancel">Cancel</Control>
@ -416,7 +412,6 @@
<Control ID="lblHost">Host:</Control>
<Control ID="lblPort">Port:</Control>
<Control ID="lblPassword">Password:</Control>
<Control ID="chkSpectator">Join as spectator</Control>
<Control ID="btnOK">OK</Control>
<Control ID="btnCancel">Cancel</Control>
</Form>

View file

@ -86,8 +86,8 @@ namespace Mesen.GUI.Emulation
{
get
{
for(int i = 0; i < 4; i++) {
switch(ConfigManager.Config.Input.Controllers[i].Type) {
for(int i = 0; i < 2; i++) {
switch(ConfigApi.GetControllerType(i)) {
case ControllerType.SuperScope:
return true;
}
@ -161,8 +161,8 @@ namespace Mesen.GUI.Emulation
return false;
}*/
for(int i = 0; i < 4; i++) {
switch(ConfigManager.Config.Input.Controllers[i].Type) {
for(int i = 0; i < 2; i++) {
switch(ConfigApi.GetControllerType(i)) {
case ControllerType.SnesMouse:
return true;
}

View file

@ -36,7 +36,6 @@ namespace Mesen.GUI.Emulation
_romPath = romPath;
if(EmuApi.LoadRom(romPath, patchPath)) {
ConfigManager.Config.RecentFiles.AddRecentFile(romPath, patchPath);
StartEmulation();
}
}
@ -109,7 +108,7 @@ namespace Mesen.GUI.Emulation
}
}
private static void StartEmulation()
public static void StartEmulation()
{
_emuThread = new Thread(() => {
EmuApi.Run();

View file

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms.NetPlay
{
public partial class frmClientConfig : BaseConfigForm
{
public frmClientConfig()
{
InitializeComponent();
Entity = ConfigManager.Config.Netplay;
AddBinding(nameof(NetplayConfig.Host), txtHost);
AddBinding(nameof(NetplayConfig.Password), txtPassword);
this.txtPort.Text = ConfigManager.Config.Netplay.Port.ToString();
}
protected override void UpdateConfig()
{
((NetplayConfig)Entity).Port = Convert.ToUInt16(txtPort.Text);
}
private void Field_TextChanged(object sender, EventArgs e)
{
UInt16 port;
if(!UInt16.TryParse(txtPort.Text, out port)) {
btnOK.Enabled = false;
} else {
btnOK.Enabled = !string.IsNullOrWhiteSpace(txtHost.Text);
}
}
}
}

View file

@ -0,0 +1,158 @@
namespace Mesen.GUI.Forms.NetPlay
{
partial class frmClientConfig
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.txtPassword = new System.Windows.Forms.TextBox();
this.txtPort = new System.Windows.Forms.TextBox();
this.lblHost = new System.Windows.Forms.Label();
this.lblPort = new System.Windows.Forms.Label();
this.txtHost = new System.Windows.Forms.TextBox();
this.lblPassword = new System.Windows.Forms.Label();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 110);
this.baseConfigPanel.Size = new System.Drawing.Size(290, 29);
this.baseConfigPanel.TabIndex = 4;
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.txtPassword, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.txtPort, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.lblHost, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.lblPort, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.txtHost, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.lblPassword, 0, 2);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 5;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(290, 110);
this.tableLayoutPanel1.TabIndex = 0;
//
// txtPassword
//
this.txtPassword.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtPassword.Location = new System.Drawing.Point(65, 55);
this.txtPassword.Name = "txtPassword";
this.txtPassword.Size = new System.Drawing.Size(222, 20);
this.txtPassword.TabIndex = 9;
//
// txtPort
//
this.txtPort.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtPort.Location = new System.Drawing.Point(65, 29);
this.txtPort.Name = "txtPort";
this.txtPort.Size = new System.Drawing.Size(222, 20);
this.txtPort.TabIndex = 6;
this.txtPort.TextChanged += new System.EventHandler(this.Field_TextChanged);
//
// lblHost
//
this.lblHost.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblHost.AutoSize = true;
this.lblHost.Location = new System.Drawing.Point(3, 6);
this.lblHost.Name = "lblHost";
this.lblHost.Size = new System.Drawing.Size(32, 13);
this.lblHost.TabIndex = 3;
this.lblHost.Text = "Host:";
//
// lblPort
//
this.lblPort.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPort.AutoSize = true;
this.lblPort.Location = new System.Drawing.Point(3, 32);
this.lblPort.Name = "lblPort";
this.lblPort.Size = new System.Drawing.Size(29, 13);
this.lblPort.TabIndex = 4;
this.lblPort.Text = "Port:";
//
// txtHost
//
this.txtHost.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtHost.Location = new System.Drawing.Point(65, 3);
this.txtHost.Name = "txtHost";
this.txtHost.Size = new System.Drawing.Size(222, 20);
this.txtHost.TabIndex = 5;
this.txtHost.TextChanged += new System.EventHandler(this.Field_TextChanged);
//
// lblPassword
//
this.lblPassword.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPassword.AutoSize = true;
this.lblPassword.Location = new System.Drawing.Point(3, 58);
this.lblPassword.Name = "lblPassword";
this.lblPassword.Size = new System.Drawing.Size(56, 13);
this.lblPassword.TabIndex = 8;
this.lblPassword.Text = "Password:";
//
// frmClientConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(290, 139);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(306, 178);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(306, 178);
this.Name = "frmClientConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Connect...";
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.Controls.SetChildIndex(this.tableLayoutPanel1, 0);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.TextBox txtPort;
private System.Windows.Forms.Label lblHost;
private System.Windows.Forms.Label lblPort;
private System.Windows.Forms.TextBox txtHost;
private System.Windows.Forms.TextBox txtPassword;
private System.Windows.Forms.Label lblPassword;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms.NetPlay
{
public partial class frmPlayerProfile : BaseConfigForm
{
public frmPlayerProfile()
{
InitializeComponent();
Entity = ConfigManager.Config.Netplay;
AddBinding(nameof(NetplayConfig.PlayerName), txtPlayerName);
}
}
}

View file

@ -0,0 +1,104 @@
namespace Mesen.GUI.Forms.NetPlay
{
partial class frmPlayerProfile
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.lblName = new System.Windows.Forms.Label();
this.txtPlayerName = new System.Windows.Forms.TextBox();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 55);
this.baseConfigPanel.Size = new System.Drawing.Size(302, 29);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.lblName, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.txtPlayerName, 1, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(302, 55);
this.tableLayoutPanel1.TabIndex = 1;
//
// lblName
//
this.lblName.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblName.AutoSize = true;
this.lblName.Location = new System.Drawing.Point(3, 6);
this.lblName.Name = "lblName";
this.lblName.Size = new System.Drawing.Size(68, 13);
this.lblName.TabIndex = 3;
this.lblName.Text = "Player name:";
//
// txtPlayerName
//
this.txtPlayerName.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtPlayerName.Location = new System.Drawing.Point(77, 3);
this.txtPlayerName.Name = "txtPlayerName";
this.txtPlayerName.Size = new System.Drawing.Size(222, 20);
this.txtPlayerName.TabIndex = 5;
this.txtPlayerName.Text = "DefaultPlayer";
//
// frmPlayerProfile
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(302, 84);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "frmPlayerProfile";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Profile";
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.Controls.SetChildIndex(this.tableLayoutPanel1, 0);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label lblName;
private System.Windows.Forms.TextBox txtPlayerName;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms.NetPlay
{
public partial class frmServerConfig : BaseConfigForm
{
public frmServerConfig()
{
InitializeComponent();
Entity = ConfigManager.Config.Netplay;
AddBinding(nameof(NetplayConfig.ServerName), txtServerName);
AddBinding(nameof(NetplayConfig.ServerPassword), txtPassword);
AddBinding(nameof(NetplayConfig.ServerPort), txtPort, eNumberFormat.Decimal);
}
private void Field_ValueChanged(object sender, EventArgs e)
{
UInt16 port;
if(!UInt16.TryParse(txtPort.Text, out port)) {
btnOK.Enabled = false;
} else {
btnOK.Enabled = !string.IsNullOrWhiteSpace(txtServerName.Text);
}
}
}
}

View file

@ -0,0 +1,158 @@
using Mesen.GUI.Controls;
namespace Mesen.GUI.Forms.NetPlay
{
partial class frmServerConfig
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tlpMain = new System.Windows.Forms.TableLayoutPanel();
this.txtPort = new System.Windows.Forms.TextBox();
this.lblPort = new System.Windows.Forms.Label();
this.lblServerName = new System.Windows.Forms.Label();
this.txtServerName = new System.Windows.Forms.TextBox();
this.lblPassword = new System.Windows.Forms.Label();
this.txtPassword = new System.Windows.Forms.TextBox();
this.tlpMain.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 98);
this.baseConfigPanel.Size = new System.Drawing.Size(302, 29);
//
// tlpMain
//
this.tlpMain.ColumnCount = 2;
this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tlpMain.Controls.Add(this.txtPort, 1, 1);
this.tlpMain.Controls.Add(this.lblPort, 0, 1);
this.tlpMain.Controls.Add(this.lblServerName, 0, 0);
this.tlpMain.Controls.Add(this.txtServerName, 1, 0);
this.tlpMain.Controls.Add(this.lblPassword, 0, 2);
this.tlpMain.Controls.Add(this.txtPassword, 1, 2);
this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tlpMain.Location = new System.Drawing.Point(0, 0);
this.tlpMain.Name = "tlpMain";
this.tlpMain.RowCount = 4;
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tlpMain.Size = new System.Drawing.Size(302, 98);
this.tlpMain.TabIndex = 1;
//
// txtPort
//
this.txtPort.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtPort.Location = new System.Drawing.Point(79, 29);
this.txtPort.Name = "txtPort";
this.txtPort.Size = new System.Drawing.Size(220, 20);
this.txtPort.TabIndex = 13;
this.txtPort.TextChanged += new System.EventHandler(this.Field_ValueChanged);
//
// lblPort
//
this.lblPort.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPort.AutoSize = true;
this.lblPort.Location = new System.Drawing.Point(3, 32);
this.lblPort.Name = "lblPort";
this.lblPort.Size = new System.Drawing.Size(29, 13);
this.lblPort.TabIndex = 12;
this.lblPort.Text = "Port:";
//
// lblServerName
//
this.lblServerName.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblServerName.AutoSize = true;
this.lblServerName.Location = new System.Drawing.Point(3, 6);
this.lblServerName.Name = "lblServerName";
this.lblServerName.Size = new System.Drawing.Size(70, 13);
this.lblServerName.TabIndex = 3;
this.lblServerName.Text = "Server name:";
//
// txtServerName
//
this.txtServerName.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtServerName.Location = new System.Drawing.Point(79, 3);
this.txtServerName.Name = "txtServerName";
this.txtServerName.Size = new System.Drawing.Size(220, 20);
this.txtServerName.TabIndex = 5;
this.txtServerName.TextChanged += new System.EventHandler(this.Field_ValueChanged);
//
// lblPassword
//
this.lblPassword.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPassword.AutoSize = true;
this.lblPassword.Location = new System.Drawing.Point(3, 58);
this.lblPassword.Name = "lblPassword";
this.lblPassword.Size = new System.Drawing.Size(56, 13);
this.lblPassword.TabIndex = 9;
this.lblPassword.Text = "Password:";
//
// txtPassword
//
this.txtPassword.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtPassword.Location = new System.Drawing.Point(79, 55);
this.txtPassword.Name = "txtPassword";
this.txtPassword.Size = new System.Drawing.Size(220, 20);
this.txtPassword.TabIndex = 10;
this.txtPassword.UseSystemPasswordChar = true;
this.txtPassword.TextChanged += new System.EventHandler(this.Field_ValueChanged);
//
// frmServerConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(302, 127);
this.Controls.Add(this.tlpMain);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "frmServerConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Server Configuration";
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.Controls.SetChildIndex(this.tlpMain, 0);
this.tlpMain.ResumeLayout(false);
this.tlpMain.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tlpMain;
private System.Windows.Forms.Label lblServerName;
private System.Windows.Forms.TextBox txtServerName;
private System.Windows.Forms.Label lblPassword;
private System.Windows.Forms.TextBox txtPassword;
private System.Windows.Forms.Label lblPort;
private System.Windows.Forms.TextBox txtPort;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -158,6 +158,19 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.pnlRenderer = new System.Windows.Forms.Panel();
this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames();
this.mnuNetPlay = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStartServer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuConnect = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlaySelectController = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer1 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer2 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer3 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer4 = new System.Windows.Forms.ToolStripMenuItem();
this.mnuNetPlayPlayer5 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuMain.SuspendLayout();
this.pnlRenderer.SuspendLayout();
this.SuspendLayout();
@ -788,6 +801,7 @@
this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuCheats,
this.mnuMovies,
this.mnuNetPlay,
this.toolStripMenuItem25,
this.mnuSoundRecorder,
this.mnuVideoRecorder,
@ -826,7 +840,7 @@
//
this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuPlayMovie.Name = "mnuPlayMovie";
this.mnuPlayMovie.Size = new System.Drawing.Size(120, 22);
this.mnuPlayMovie.Size = new System.Drawing.Size(152, 22);
this.mnuPlayMovie.Text = "Play...";
this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click);
//
@ -834,7 +848,7 @@
//
this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record;
this.mnuRecordMovie.Name = "mnuRecordMovie";
this.mnuRecordMovie.Size = new System.Drawing.Size(120, 22);
this.mnuRecordMovie.Size = new System.Drawing.Size(152, 22);
this.mnuRecordMovie.Text = "Record...";
this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click);
//
@ -842,7 +856,7 @@
//
this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuStopMovie.Name = "mnuStopMovie";
this.mnuStopMovie.Size = new System.Drawing.Size(120, 22);
this.mnuStopMovie.Size = new System.Drawing.Size(152, 22);
this.mnuStopMovie.Text = "Stop";
this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click);
//
@ -1163,6 +1177,97 @@
this.ctrlRecentGames.TabIndex = 1;
this.ctrlRecentGames.Visible = false;
//
// mnuNetPlay
//
this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuStartServer,
this.mnuConnect,
this.mnuNetPlaySelectController,
this.toolStripSeparator2,
this.mnuProfile});
this.mnuNetPlay.Image = global::Mesen.GUI.Properties.Resources.Network;
this.mnuNetPlay.Name = "mnuNetPlay";
this.mnuNetPlay.Size = new System.Drawing.Size(182, 22);
this.mnuNetPlay.Text = "Net Play";
//
// mnuStartServer
//
this.mnuStartServer.Name = "mnuStartServer";
this.mnuStartServer.Size = new System.Drawing.Size(168, 22);
this.mnuStartServer.Text = "Start Server";
//
// mnuConnect
//
this.mnuConnect.Name = "mnuConnect";
this.mnuConnect.Size = new System.Drawing.Size(168, 22);
this.mnuConnect.Text = "Connect to Server";
//
// mnuNetPlaySelectController
//
this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuNetPlayPlayer1,
this.mnuNetPlayPlayer2,
this.mnuNetPlayPlayer3,
this.mnuNetPlayPlayer4,
this.mnuNetPlayPlayer5,
this.toolStripSeparator1,
this.mnuNetPlaySpectator});
this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController";
this.mnuNetPlaySelectController.Size = new System.Drawing.Size(168, 22);
this.mnuNetPlaySelectController.Text = "Select Controller";
//
// mnuNetPlayPlayer1
//
this.mnuNetPlayPlayer1.Name = "mnuNetPlayPlayer1";
this.mnuNetPlayPlayer1.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer1.Text = "Player 1";
//
// mnuNetPlayPlayer2
//
this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2";
this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer2.Text = "Player 2";
//
// mnuNetPlayPlayer3
//
this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3";
this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer3.Text = "Player 3";
//
// mnuNetPlayPlayer4
//
this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4";
this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer4.Text = "Player 4";
//
// mnuNetPlayPlayer5
//
this.mnuNetPlayPlayer5.Name = "mnuNetPlayPlayer5";
this.mnuNetPlayPlayer5.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer5.Text = "Player 5";
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
//
// mnuNetPlaySpectator
//
this.mnuNetPlaySpectator.Name = "mnuNetPlaySpectator";
this.mnuNetPlaySpectator.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlaySpectator.Text = "Spectator";
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(165, 6);
//
// mnuProfile
//
this.mnuProfile.Name = "mnuProfile";
this.mnuProfile.Size = new System.Drawing.Size(168, 22);
this.mnuProfile.Text = "Configure Profile";
//
// frmMain
//
this.AllowDrop = true;
@ -1319,5 +1424,18 @@
private System.Windows.Forms.ToolStripMenuItem mnuTestRecord;
private System.Windows.Forms.ToolStripMenuItem mnuTestStop;
private System.Windows.Forms.ToolStripMenuItem mnuRunAllTests;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlay;
private System.Windows.Forms.ToolStripMenuItem mnuStartServer;
private System.Windows.Forms.ToolStripMenuItem mnuConnect;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlaySelectController;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer1;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer2;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer3;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer4;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlayPlayer5;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlaySpectator;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem mnuProfile;
}
}

View file

@ -4,6 +4,7 @@ using Mesen.GUI.Debugger;
using Mesen.GUI.Debugger.Workspace;
using Mesen.GUI.Emulation;
using Mesen.GUI.Forms.Config;
using Mesen.GUI.Forms.NetPlay;
using Mesen.GUI.Interop;
using Mesen.GUI.Updates;
using Mesen.GUI.Utilities;
@ -66,6 +67,13 @@ namespace Mesen.GUI.Forms
ConfigManager.Config.Video.ApplyConfig();
EmuApi.InitializeEmu(ConfigManager.HomeFolder, Handle, ctrlRenderer.Handle, false, false, false);
if(ConfigManager.Config.Preferences.OverrideGameFolder && Directory.Exists(ConfigManager.Config.Preferences.GameFolder)) {
EmuApi.AddKnownGameFolder(ConfigManager.Config.Preferences.GameFolder);
}
foreach(RecentItem recentItem in ConfigManager.Config.RecentFiles.Items) {
EmuApi.AddKnownGameFolder(recentItem.RomFile.Folder);
}
ConfigManager.Config.InitializeDefaults();
ConfigManager.Config.ApplyConfig();
@ -152,6 +160,10 @@ namespace Mesen.GUI.Forms
CheatCodes.ApplyCheats();
this.BeginInvoke((Action)(() => {
if(!EmuRunner.IsRunning()) {
EmuRunner.StartEmulation();
}
UpdateDebuggerMenu();
ctrlRecentGames.Visible = false;
SaveStateManager.UpdateStateMenu(mnuLoadState, false);
@ -313,6 +325,15 @@ namespace Mesen.GUI.Forms
mnuCheats.Click += (s, e) => { frmCheatList.ShowWindow(); };
mnuOptions.DropDownOpening += (s, e) => {
bool isConnected = NetplayApi.IsConnected();
mnuRegion.Enabled = !isConnected;
mnuInputConfig.Enabled = !isConnected;
mnuEmulationConfig.Enabled = !isConnected;
};
InitNetPlayMenus();
mnuDebugger.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.Debugger); };
mnuSpcDebugger.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.SpcDebugger); };
mnuSa1Debugger.Click += (s, e) => { DebugWindowManager.OpenDebugWindow(DebugWindow.Sa1Debugger); };
@ -335,6 +356,60 @@ namespace Mesen.GUI.Forms
UpdateDebuggerMenu();
}
private void InitNetPlayMenus()
{
mnuConnect.Click += (s, e) => { NetPlayHelper.Connect(); };
mnuStartServer.Click += (s, e) => { NetPlayHelper.ToggleServer(); };
mnuProfile.Click += (s, e) => { using(frmPlayerProfile frm = new frmPlayerProfile()) { frm.ShowDialog(); } };
mnuNetPlayPlayer1.Click += (s, e) => { NetplayApi.NetPlaySelectController(0); };
mnuNetPlayPlayer2.Click += (s, e) => { NetplayApi.NetPlaySelectController(1); };
mnuNetPlayPlayer3.Click += (s, e) => { NetplayApi.NetPlaySelectController(2); };
mnuNetPlayPlayer4.Click += (s, e) => { NetplayApi.NetPlaySelectController(3); };
mnuNetPlayPlayer5.Click += (s, e) => { NetplayApi.NetPlaySelectController(4); };
mnuNetPlaySpectator.Click += (s, e) => { NetplayApi.NetPlaySelectController(0xFF); };
mnuNetPlay.DropDownOpening += (s, e) => {
bool isClient = NetplayApi.IsConnected();
bool isServer = NetplayApi.IsServerRunning();
mnuConnect.Text = ResourceHelper.GetMessage(isClient ? "Disconnect" : "ConnectToServer");
mnuConnect.Enabled = !isServer;
mnuStartServer.Text = ResourceHelper.GetMessage(isServer ? "StopServer" : "StartServer");
mnuStartServer.Enabled = !isClient;
mnuNetPlaySelectController.Enabled = isClient || isServer;
};
mnuNetPlaySelectController.DropDownOpening += (s, e) => {
int availableControllers = NetplayApi.NetPlayGetAvailableControllers();
int currentControllerPort = NetplayApi.NetPlayGetControllerPort();
mnuNetPlayPlayer1.Enabled = (availableControllers & 0x01) == 0x01;
mnuNetPlayPlayer2.Enabled = (availableControllers & 0x02) == 0x02;
mnuNetPlayPlayer3.Enabled = (availableControllers & 0x04) == 0x04;
mnuNetPlayPlayer4.Enabled = (availableControllers & 0x08) == 0x08;
mnuNetPlayPlayer5.Enabled = (availableControllers & 0x10) == 0x10;
Func<int, string> getControllerName = (int port) => {
ControllerType type = ConfigApi.GetControllerType(port);
if(type == ControllerType.Multitap) {
type = ControllerType.SnesController;
}
return ResourceHelper.GetEnumText(type);
};
mnuNetPlayPlayer1.Text = ResourceHelper.GetMessage("PlayerNumber", "1") + " (" + getControllerName(0) + ")";
mnuNetPlayPlayer2.Text = ResourceHelper.GetMessage("PlayerNumber", "2") + " (" + getControllerName(1) + ")";
mnuNetPlayPlayer3.Text = ResourceHelper.GetMessage("PlayerNumber", "3") + " (" + getControllerName(2) + ")";
mnuNetPlayPlayer4.Text = ResourceHelper.GetMessage("PlayerNumber", "4") + " (" + getControllerName(3) + ")";
mnuNetPlayPlayer5.Text = ResourceHelper.GetMessage("PlayerNumber", "5") + " (" + getControllerName(4) + ")";
mnuNetPlayPlayer1.Checked = (currentControllerPort == 0);
mnuNetPlayPlayer2.Checked = (currentControllerPort == 1);
mnuNetPlayPlayer3.Checked = (currentControllerPort == 2);
mnuNetPlayPlayer4.Checked = (currentControllerPort == 3);
mnuNetPlayPlayer5.Checked = (currentControllerPort == 4);
mnuNetPlaySpectator.Checked = (currentControllerPort == 0xFF);
};
}
private void UpdateDebuggerMenu()
{
bool running = EmuRunner.IsRunning();
@ -357,8 +432,6 @@ namespace Mesen.GUI.Forms
mnuPaletteViewer.Enabled = running;
mnuEventViewer.Enabled = running;
mnuRegisterViewer.Enabled = running;
mnuCheats.Enabled = running;
}
private void ResizeRecentGames()
@ -471,6 +544,8 @@ namespace Mesen.GUI.Forms
private void mnuFile_DropDownOpening(object sender, EventArgs e)
{
mnuLoadState.Enabled = !NetplayApi.IsConnected();
mnuRecentFiles.DropDownItems.Clear();
mnuRecentFiles.DropDownItems.AddRange(ConfigManager.Config.RecentFiles.GetMenuItems().ToArray());
mnuRecentFiles.Enabled = ConfigManager.Config.RecentFiles.Items.Count > 0;
@ -551,8 +626,9 @@ namespace Mesen.GUI.Forms
private void mnuTools_DropDownOpening(object sender, EventArgs e)
{
bool isClient = NetplayApi.IsConnected();
mnuMovies.Enabled = EmuRunner.IsRunning();
mnuPlayMovie.Enabled = EmuRunner.IsRunning() && !RecordApi.MoviePlaying() && !RecordApi.MovieRecording();
mnuPlayMovie.Enabled = EmuRunner.IsRunning() && !RecordApi.MoviePlaying() && !RecordApi.MovieRecording() && !isClient;
mnuRecordMovie.Enabled = EmuRunner.IsRunning() && !RecordApi.MoviePlaying() && !RecordApi.MovieRecording();
mnuStopMovie.Enabled = EmuRunner.IsRunning() && (RecordApi.MoviePlaying() || RecordApi.MovieRecording());
@ -563,6 +639,8 @@ namespace Mesen.GUI.Forms
mnuVideoRecorder.Enabled = EmuRunner.IsRunning();
mnuAviRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.AviIsRecording();
mnuAviStop.Enabled = EmuRunner.IsRunning() && RecordApi.AviIsRecording();
mnuCheats.Enabled = EmuRunner.IsRunning() && !isClient;
}
private void mnuAviRecord_Click(object sender, EventArgs e)

View file

@ -28,6 +28,8 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void SetEmulationFlag(EmulationFlags flag, bool enabled);
[DllImport(DllPath)] public static extern void SetDebuggerFlag(DebuggerFlags flag, bool enabled);
[DllImport(DllPath)] public static extern ControllerType GetControllerType(int player);
[DllImport(DllPath, EntryPoint = "GetAudioDevices")] private static extern IntPtr GetAudioDevicesWrapper();
public static List<string> GetAudioDevices()
{

23
UI/Interop/NetplayApi.cs Normal file
View file

@ -0,0 +1,23 @@
using Mesen.GUI.Config;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Mesen.GUI
{
public class NetplayApi
{
private const string DllPath = "MesenSCore.dll";
[DllImport(DllPath)] public static extern void StartServer(UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string password, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string hostPlayerName);
[DllImport(DllPath)] public static extern void StopServer();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsServerRunning();
[DllImport(DllPath)] public static extern void Connect([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string host, UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string password, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string playerName, [MarshalAs(UnmanagedType.I1)]bool spectator);
[DllImport(DllPath)] public static extern void Disconnect();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsConnected();
[DllImport(DllPath)] public static extern Int32 NetPlayGetAvailableControllers();
[DllImport(DllPath)] public static extern void NetPlaySelectController(Int32 controllerPort);
[DllImport(DllPath)] public static extern Int32 NetPlayGetControllerPort();
}
}

View file

@ -64,5 +64,6 @@ namespace Mesen.GUI
EventViewerRefresh = 14,
MissingFirmware = 15,
BeforeGameUnload = 16,
CheatsChanged = 17,
}
}

View file

@ -225,6 +225,7 @@
<Compile Include="Config\Shortcuts\KeyCombination.cs" />
<Compile Include="Config\Shortcuts\ShortcutKeyInfo.cs" />
<Compile Include="Config\EmulationConfig.cs" />
<Compile Include="Config\NetplayConfig.cs" />
<Compile Include="Config\VideoConfig.cs" />
<Compile Include="Controls\ctrlLinkLabel.cs">
<SubType>Component</SubType>
@ -793,6 +794,24 @@
<Compile Include="Forms\Config\frmRecordMovie.Designer.cs">
<DependentUpon>frmRecordMovie.cs</DependentUpon>
</Compile>
<Compile Include="Forms\NetPlay\frmClientConfig.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\NetPlay\frmClientConfig.Designer.cs">
<DependentUpon>frmClientConfig.cs</DependentUpon>
</Compile>
<Compile Include="Forms\NetPlay\frmPlayerProfile.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\NetPlay\frmPlayerProfile.Designer.cs">
<DependentUpon>frmPlayerProfile.cs</DependentUpon>
</Compile>
<Compile Include="Forms\NetPlay\frmServerConfig.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\NetPlay\frmServerConfig.Designer.cs">
<DependentUpon>frmServerConfig.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Tools\CheatDatabase.cs" />
<Compile Include="Forms\Tools\frmCheat.cs">
<SubType>Form</SubType>
@ -837,6 +856,7 @@
<DependentUpon>frmRecordAvi.cs</DependentUpon>
</Compile>
<Compile Include="Interop\DebugState.cs" />
<Compile Include="Interop\NetplayApi.cs" />
<Compile Include="Interop\RecordApi.cs" />
<Compile Include="Interop\TestApi.cs" />
<Compile Include="Updates\frmDownloadProgress.cs">
@ -876,6 +896,7 @@
<Compile Include="Utilities\InBackgroundHelper.cs" />
<Compile Include="Utilities\Md5Helper.cs" />
<Compile Include="Updates\UpdateHelper.cs" />
<Compile Include="Utilities\NetPlayHelper.cs" />
<Compile Include="Utilities\RandomGameHelper.cs" />
<Compile Include="Utilities\RomTestHelper.cs" />
<Compile Include="Utilities\XmlColor.cs" />
@ -1106,6 +1127,15 @@
<EmbeddedResource Include="Forms\Config\frmRecordMovie.resx">
<DependentUpon>frmRecordMovie.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\NetPlay\frmClientConfig.resx">
<DependentUpon>frmClientConfig.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\NetPlay\frmPlayerProfile.resx">
<DependentUpon>frmPlayerProfile.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\NetPlay\frmServerConfig.resx">
<DependentUpon>frmServerConfig.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Tools\frmCheat.resx">
<DependentUpon>frmCheat.cs</DependentUpon>
</EmbeddedResource>

View file

@ -0,0 +1,45 @@
using Mesen.GUI.Config;
using Mesen.GUI.Forms;
using Mesen.GUI.Forms.NetPlay;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Utilities
{
public static class NetPlayHelper
{
public static void Connect()
{
if(NetplayApi.IsConnected()) {
Task.Run(() => NetplayApi.Disconnect());
} else {
using(frmClientConfig frm = new frmClientConfig()) {
if(frm.ShowDialog(frmMain.Instance) == DialogResult.OK) {
NetplayConfig cfg = ConfigManager.Config.Netplay;
Task.Run(() => {
NetplayApi.Connect(cfg.Host, cfg.Port, cfg.Password, cfg.PlayerName, false);
});
}
}
}
}
public static void ToggleServer()
{
if(NetplayApi.IsServerRunning()) {
Task.Run(() => NetplayApi.StopServer());
} else {
using(frmServerConfig frm = new frmServerConfig()) {
if(frm.ShowDialog(frmMain.Instance) == DialogResult.OK) {
NetplayConfig cfg = ConfigManager.Config.Netplay;
NetplayApi.StartServer(cfg.ServerPort, cfg.ServerPassword, cfg.PlayerName);
}
}
}
}
}
}

View file

@ -36,13 +36,8 @@ using namespace std;
#define ioctlsocket ioctl
#endif
#define BUFFER_SIZE 200000
Socket::Socket()
{
_sendBuffer = new char[BUFFER_SIZE];
_bufferPosition = 0;
#ifdef _WIN32
WSADATA wsaDat;
if(WSAStartup(MAKEWORD(2, 2), &wsaDat) != 0) {
@ -71,9 +66,6 @@ Socket::Socket(uintptr_t socket)
} else {
SetSocketOptions();
}
_sendBuffer = new char[BUFFER_SIZE];
_bufferPosition = 0;
}
Socket::~Socket()
@ -91,8 +83,6 @@ Socket::~Socket()
WSACleanup();
}
#endif
delete[] _sendBuffer;
}
void Socket::SetSocketOptions()
@ -256,22 +246,6 @@ int Socket::Send(char *buf, int len, int flags)
return returnVal;
}
void Socket::BufferedSend(char *buf, int len)
{
if(_bufferPosition+len < BUFFER_SIZE) {
memcpy(_sendBuffer+_bufferPosition, buf, len);
_bufferPosition += len;
} else {
std::cout << "prevented buffer overflow";
}
}
void Socket::SendBuffer()
{
Send(_sendBuffer, _bufferPosition, 0);
_bufferPosition = 0;
}
int Socket::Recv(char *buf, int len, int flags)
{
int returnVal = recv(_socket, buf, len, flags);

View file

@ -12,8 +12,6 @@ private:
uintptr_t _socket = ~0;
bool _connectionError = false;
char* _sendBuffer;
int _bufferPosition;
int32_t _UPnPPort = -1;
#endif