Netplay: Added support for netplay
This commit is contained in:
parent
83868031bb
commit
4f22eefa21
69 changed files with 3434 additions and 76 deletions
|
@ -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)
|
||||
|
|
24
Core/ClientConnectionData.h
Normal file
24
Core/ClientConnectionData.h
Normal 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()
|
||||
{
|
||||
}
|
||||
};
|
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
32
Core/ForceDisconnectMessage.h
Normal file
32
Core/ForceDisconnectMessage.h
Normal 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
120
Core/GameClient.cpp
Normal 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
42
Core/GameClient.h
Normal 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;
|
||||
};
|
267
Core/GameClientConnection.cpp
Normal file
267
Core/GameClientConnection.cpp
Normal 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;
|
||||
}
|
62
Core/GameClientConnection.h
Normal file
62
Core/GameClientConnection.h
Normal 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
99
Core/GameConnection.cpp
Normal 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
48
Core/GameConnection.h
Normal 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);
|
||||
};
|
55
Core/GameInformationMessage.h
Normal file
55
Core/GameInformationMessage.h
Normal 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
258
Core/GameServer.cpp
Normal 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
58
Core/GameServer.h
Normal 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;
|
||||
};
|
256
Core/GameServerConnection.cpp
Normal file
256
Core/GameServerConnection.cpp
Normal 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;
|
||||
}
|
52
Core/GameServerConnection.h
Normal file
52
Core/GameServerConnection.h
Normal 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
66
Core/HandShakeMessage.h
Normal 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);
|
||||
}
|
||||
};
|
|
@ -20,6 +20,7 @@ enum class ConsoleNotificationType
|
|||
EventViewerRefresh = 14,
|
||||
MissingFirmware = 15,
|
||||
BeforeGameUnload = 16,
|
||||
CheatsChanged = 17
|
||||
};
|
||||
|
||||
class INotificationListener
|
||||
|
|
29
Core/InputDataMessage.h
Normal file
29
Core/InputDataMessage.h
Normal 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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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
15
Core/MessageType.h
Normal 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
37
Core/MovieDataMessage.h
Normal 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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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
139
Core/NetMessage.h
Normal 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
64
Core/PlayerListMessage.h
Normal 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
49
Core/RomFinder.h
Normal 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 "";
|
||||
}
|
||||
};
|
|
@ -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
102
Core/SaveStateMessage.h
Normal 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);
|
||||
}
|
||||
};
|
28
Core/SelectControllerMessage.h
Normal file
28
Core/SelectControllerMessage.h
Normal 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;
|
||||
}
|
||||
};
|
28
Core/ServerInformationMessage.h
Normal file
28
Core/ServerInformationMessage.h
Normal 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);
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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() : "";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -43,5 +43,8 @@
|
|||
<ClCompile Include="TestApiWrapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NetplayApiWrapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
50
InteropDLL/NetplayApiWrapper.cpp
Normal file
50
InteropDLL/NetplayApiWrapper.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
22
UI/Config/NetplayConfig.cs
Normal file
22
UI/Config/NetplayConfig.cs
Normal 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 = "";
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
43
UI/Forms/NetPlay/frmClientConfig.cs
Normal file
43
UI/Forms/NetPlay/frmClientConfig.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
UI/Forms/NetPlay/frmClientConfig.designer.cs
generated
Normal file
158
UI/Forms/NetPlay/frmClientConfig.designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
123
UI/Forms/NetPlay/frmClientConfig.resx
Normal file
123
UI/Forms/NetPlay/frmClientConfig.resx
Normal 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>
|
24
UI/Forms/NetPlay/frmPlayerProfile.cs
Normal file
24
UI/Forms/NetPlay/frmPlayerProfile.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
104
UI/Forms/NetPlay/frmPlayerProfile.designer.cs
generated
Normal file
104
UI/Forms/NetPlay/frmPlayerProfile.designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
123
UI/Forms/NetPlay/frmPlayerProfile.resx
Normal file
123
UI/Forms/NetPlay/frmPlayerProfile.resx
Normal 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>
|
37
UI/Forms/NetPlay/frmServerConfig.cs
Normal file
37
UI/Forms/NetPlay/frmServerConfig.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
UI/Forms/NetPlay/frmServerConfig.designer.cs
generated
Normal file
158
UI/Forms/NetPlay/frmServerConfig.designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
123
UI/Forms/NetPlay/frmServerConfig.resx
Normal file
123
UI/Forms/NetPlay/frmServerConfig.resx
Normal 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>
|
124
UI/Forms/frmMain.Designer.cs
generated
124
UI/Forms/frmMain.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
23
UI/Interop/NetplayApi.cs
Normal 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();
|
||||
}
|
||||
}
|
|
@ -64,5 +64,6 @@ namespace Mesen.GUI
|
|||
EventViewerRefresh = 14,
|
||||
MissingFirmware = 15,
|
||||
BeforeGameUnload = 16,
|
||||
CheatsChanged = 17,
|
||||
}
|
||||
}
|
||||
|
|
30
UI/UI.csproj
30
UI/UI.csproj
|
@ -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>
|
||||
|
|
45
UI/Utilities/NetPlayHelper.cs
Normal file
45
UI/Utilities/NetPlayHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -12,8 +12,6 @@ private:
|
|||
|
||||
uintptr_t _socket = ~0;
|
||||
bool _connectionError = false;
|
||||
char* _sendBuffer;
|
||||
int _bufferPosition;
|
||||
int32_t _UPnPPort = -1;
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue