NetPlay: Allow controller selection for host & clients + Spectator mode

This commit is contained in:
Souryo 2016-02-06 15:33:45 -05:00
parent 361f4b8025
commit 3e798af865
27 changed files with 594 additions and 55 deletions

View file

@ -12,8 +12,10 @@ public:
uint8_t* AvatarData;
uint32_t AvatarSize;
ClientConnectionData(string host, uint16_t port, string playerName, uint8_t* avatarData, uint32_t avatarSize) :
Host(host), Port(port), PlayerName(playerName), AvatarSize(avatarSize)
bool Spectator;
ClientConnectionData(string host, uint16_t port, string playerName, uint8_t* avatarData, uint32_t avatarSize, bool spectator) :
Host(host), Port(port), PlayerName(playerName), AvatarSize(avatarSize), Spectator(spectator)
{
if(avatarSize > 0) {
AvatarData = new uint8_t[avatarSize];

View file

@ -407,9 +407,11 @@
<ClInclude Include="MMC3_52.h" />
<ClInclude Include="MMC3_ChrRam.h" />
<ClInclude Include="ModChannel.h" />
<ClInclude Include="PlayerListMessage.h" />
<ClInclude Include="RomData.h" />
<ClInclude Include="NtdecTc112.h" />
<ClInclude Include="Rambo1.h" />
<ClInclude Include="SelectControllerMessage.h" />
<ClInclude Include="SoundMixer.h" />
<ClInclude Include="Namco108.h" />
<ClInclude Include="Namco108_154.h" />

View file

@ -539,6 +539,12 @@
<ClInclude Include="BaseControlDevice.h">
<Filter>Nes\Controllers</Filter>
</ClInclude>
<ClInclude Include="PlayerListMessage.h">
<Filter>NetPlay\Messages</Filter>
</ClInclude>
<ClInclude Include="SelectControllerMessage.h">
<Filter>NetPlay\Messages</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View file

@ -85,5 +85,34 @@ void GameClient::ProcessNotification(ConsoleNotificationType type, void* paramet
uint8_t GameClient::GetControllerState(uint8_t port)
{
return Instance->_connection->GetControllerState(port);
if(Instance && Instance->_connection) {
return Instance->_connection->GetControllerState(port);
}
return 0;
}
void GameClient::SelectController(uint8_t port)
{
if(Instance && Instance->_connection) {
Instance->_connection->SelectController(port);
}
}
uint8_t GameClient::GetAvailableControllers()
{
if(Instance && Instance->_connection) {
return Instance->_connection->GetAvailableControllers();
}
return 0;
}
uint8_t GameClient::GetControllerPort()
{
if(Instance && Instance->_connection) {
return Instance->_connection->GetControllerPort();
}
return GameConnection::SpectatorPort;
}

View file

@ -29,6 +29,10 @@ public:
static void Connect(shared_ptr<ClientConnectionData> connectionData);
static void Disconnect();
static void SelectController(uint8_t port);
static uint8_t GetControllerPort();
static uint8_t GetAvailableControllers();
static uint8_t GetControllerState(uint8_t port);
void ProcessNotification(ConsoleNotificationType type, void* parameter);

View file

@ -10,6 +10,8 @@
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "StandardController.h"
#include "SelectControllerMessage.h"
#include "PlayerListMessage.h"
GameClientConnection::GameClientConnection(shared_ptr<Socket> socket, shared_ptr<ClientConnectionData> connectionData) : GameConnection(socket, connectionData)
{
@ -28,7 +30,13 @@ GameClientConnection::~GameClientConnection()
void GameClientConnection::SendHandshake()
{
HandShakeMessage message(_connectionData->PlayerName, _connectionData->AvatarData, _connectionData->AvatarSize);
HandShakeMessage message(_connectionData->PlayerName, _connectionData->AvatarData, _connectionData->AvatarSize, _connectionData->Spectator);
SendNetMessage(message);
}
void GameClientConnection::SendControllerSelection(uint8_t port)
{
SelectControllerMessage message(port);
SendNetMessage(message);
}
@ -57,21 +65,31 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
case ControllerType::Zapper: _controlDevice = ControlManager::GetControlDevice(_controllerPort); break;
}
Console::Resume();
}
break;
case MessageType::MovieData:
if(_gameLoaded) {
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(), ((MovieDataMessage*)message)->GetInputState());
}
break;
case MessageType::PlayerList:
_playerList = ((PlayerListMessage*)message)->GetPlayerList();
break;
case MessageType::GameInformation:
DisableControllers();
Console::Pause();
gameInfo = (GameInformationMessage*)message;
if(gameInfo->GetPort() != _controllerPort) {
_controllerPort = gameInfo->GetPort();
MessageManager::DisplayMessage("Net Play", string("Connected as player ") + std::to_string(_controllerPort + 1));
if(_controllerPort == GameConnection::SpectatorPort) {
MessageManager::DisplayMessage("Net Play", "Connected as spectator");
} else {
MessageManager::DisplayMessage("Net Play", string("Connected as player ") + std::to_string(_controllerPort + 1));
}
}
ClearInputData();
@ -160,4 +178,25 @@ void GameClientConnection::SendInput()
_lastInputSent = inputState;
}
}
}
void GameClientConnection::SelectController(uint8_t port)
{
SendControllerSelection(port);
}
uint8_t GameClientConnection::GetAvailableControllers()
{
uint8_t availablePorts = 0x0F;
for(PlayerInfo &playerInfo : _playerList) {
if(playerInfo.ControllerPort < 4) {
availablePorts &= ~(1 << playerInfo.ControllerPort);
}
}
return availablePorts;
}
uint8_t GameClientConnection::GetControllerPort()
{
return _controllerPort;
}

View file

@ -19,13 +19,16 @@ private:
atomic<bool> _enableControllers = false;
atomic<uint32_t> _minimumQueueSize = 3;
vector<PlayerInfo> _playerList;
shared_ptr<BaseControlDevice> _controlDevice;
uint32_t _lastInputSent = 0x00;
bool _gameLoaded = false;
uint8_t _controllerPort = 255;
uint8_t _controllerPort = GameConnection::SpectatorPort;
private:
void SendHandshake();
void SendControllerSelection(uint8_t port);
void ClearInputData();
void PushControllerState(uint8_t port, uint8_t state);
void DisableControllers();
@ -39,4 +42,8 @@ public:
uint8_t GetControllerState(uint8_t port);
void SendInput();
void SelectController(uint8_t port);
uint8_t GetAvailableControllers();
uint8_t GetControllerPort();
};

View file

@ -5,6 +5,8 @@
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "PlayerListMessage.h"
#include "SelectControllerMessage.h"
#include "ClientConnectionData.h"
GameConnection::GameConnection(shared_ptr<Socket> socket, shared_ptr<ClientConnectionData> connectionData)
@ -54,6 +56,8 @@ NetMessage* GameConnection::ReadMessage()
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);
}
}
return nullptr;

View file

@ -6,6 +6,13 @@ class Socket;
class NetMessage;
class ClientConnectionData;
struct PlayerInfo
{
string Name;
uint8_t ControllerPort;
bool IsHost;
};
class GameConnection
{
protected:
@ -24,14 +31,11 @@ private:
virtual void ProcessMessage(NetMessage* message) = 0;
protected:
void SendNetMessage(NetMessage &message);
public:
static const uint8_t SpectatorPort = 0xFF;
GameConnection(shared_ptr<Socket> socket, shared_ptr<ClientConnectionData> connectionData);
bool ConnectionError();
void ProcessMessages();
void SendNetMessage(NetMessage &message);
};

View file

@ -6,11 +6,15 @@ using std::thread;
#include "GameServer.h"
#include "Console.h"
#include "../Utilities/Socket.h"
#include "PlayerListMessage.h"
unique_ptr<GameServer> GameServer::Instance;
GameServer::GameServer()
GameServer::GameServer(uint16_t listenPort, string hostPlayerName)
{
_port = listenPort;
_hostPlayerName = hostPlayerName;
_hostControllerPort = 0;
ControlManager::RegisterBroadcaster(this);
}
@ -29,7 +33,7 @@ void GameServer::AcceptConnections()
while(true) {
shared_ptr<Socket> socket = _listener->Accept();
if(!socket->ConnectionError()) {
_openConnections.push_back(shared_ptr<GameServerConnection>(new GameServerConnection(socket, 1)));
_openConnections.push_back(shared_ptr<GameServerConnection>(new GameServerConnection(socket)));
} else {
break;
}
@ -53,6 +57,15 @@ void GameServer::UpdateConnections()
}
}
list<shared_ptr<GameServerConnection>> GameServer::GetConnectionList()
{
if(GameServer::Started()) {
return Instance->_openConnections;
} else {
return list<shared_ptr<GameServerConnection>>();
}
}
void GameServer::Exec()
{
_listener.reset(new Socket());
@ -77,10 +90,9 @@ void GameServer::Stop()
MessageManager::DisplayMessage("Net Play", "Server stopped");
}
void GameServer::StartServer(uint16_t port)
void GameServer::StartServer(uint16_t port, string hostPlayerName)
{
Instance.reset(new GameServer());
Instance->_port = port;
Instance.reset(new GameServer(port, hostPlayerName));
Instance->_serverThread.reset(new thread(&GameServer::Exec, Instance.get()));
}
@ -108,4 +120,75 @@ void GameServer::BroadcastInput(uint8_t inputData, uint8_t port)
connection->SendMovieData(inputData, port);
}
}
}
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()) {
Console::Pause();
if(port == GameConnection::SpectatorPort || GetAvailableControllers() & (1 << port)) {
//Port is available
Instance->_hostControllerPort = port;
SendPlayerList();
}
Console::Resume();
}
}
uint8_t GameServer::GetAvailableControllers()
{
uint8_t availablePorts = 0x0F;
for(PlayerInfo &playerInfo : GetPlayerList()) {
if(playerInfo.ControllerPort < 4) {
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);
}
}

View file

@ -18,6 +18,9 @@ private:
list<shared_ptr<GameServerConnection>> _openConnections;
bool _initialized = false;
string _hostPlayerName;
uint8_t _hostControllerPort;
void AcceptConnections();
void UpdateConnections();
@ -25,12 +28,21 @@ private:
void Stop();
public:
GameServer();
GameServer(uint16_t port, string hostPlayerName);
~GameServer();
static void StartServer(uint16_t port);
static void StartServer(uint16_t port, 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();
virtual void BroadcastInput(uint8_t inputData, uint8_t port);
};

View file

@ -11,13 +11,16 @@
#include "ClientConnectionData.h"
#include "EmulationSettings.h"
#include "StandardController.h"
#include "SelectControllerMessage.h"
#include "PlayerListMessage.h"
#include "GameServer.h"
GameServerConnection* GameServerConnection::_netPlayDevices[4] = { nullptr,nullptr,nullptr, nullptr };
GameServerConnection* GameServerConnection::_netPlayDevices[4] = { nullptr,nullptr,nullptr,nullptr };
GameServerConnection::GameServerConnection(shared_ptr<Socket> socket, int controllerPort) : GameConnection(socket, nullptr)
GameServerConnection::GameServerConnection(shared_ptr<Socket> socket) : GameConnection(socket, nullptr)
{
//Server-side connection
_controllerPort = controllerPort;
_controllerPort = GameConnection::SpectatorPort;
MessageManager::RegisterNotificationListener(this);
}
@ -72,34 +75,75 @@ uint32_t GameServerConnection::GetState()
return stateData;
}
void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
{
//Send the game's current state to the client and register the controller
if(message->IsValid()) {
Console::Pause();
_controllerPort = message->IsSpectator() ? GameConnection::SpectatorPort : GetFirstFreeControllerPort();
_connectionData.reset(new ClientConnectionData("", 0, message->GetPlayerName(), message->GetAvatarData(), message->GetAvatarSize(), false));
string playerPortMessage = _controllerPort == GameConnection::SpectatorPort ? "Spectator" : "Player " + std::to_string(_controllerPort + 1);
MessageManager::DisplayToast("Net Play", _connectionData->PlayerName + " (" + playerPortMessage + ") connected.", _connectionData->AvatarData, _connectionData->AvatarSize);
if(Console::GetROMPath().size() > 0) {
SendGameInformation();
}
_handshakeCompleted = true;
RegisterNetPlayDevice(this, _controllerPort);
GameServer::SendPlayerList();
Console::Resume();
}
}
void GameServerConnection::ProcessMessage(NetMessage* message)
{
switch(message->GetType()) {
case MessageType::HandShake:
//Send the game's current state to the client and register the controller
if(((HandShakeMessage*)message)->IsValid()) {
Console::Pause();
_connectionData.reset(new ClientConnectionData("", 0, ((HandShakeMessage*)message)->GetPlayerName(), ((HandShakeMessage*)message)->GetAvatarData(), ((HandShakeMessage*)message)->GetAvatarSize()));
MessageManager::DisplayToast("Net Play", _connectionData->PlayerName + " (Player " + std::to_string(_controllerPort + 1) + ") connected.", _connectionData->AvatarData, _connectionData->AvatarSize);
if(Console::GetROMPath().size() > 0) {
SendGameInformation();
}
_handshakeCompleted = true;
RegisterNetPlayDevice(this, _controllerPort);
Console::Resume();
}
ProcessHandshakeResponse((HandShakeMessage*)message);
break;
case MessageType::InputData:
PushState(((InputDataMessage*)message)->GetInputState());
break;
case MessageType::SelectController:
SelectControllerPort(((SelectControllerMessage*)message)->GetPortNumber());
break;
default:
break;
}
}
void GameServerConnection::SelectControllerPort(uint8_t port)
{
Console::Pause();
if(port == GameConnection::SpectatorPort) {
//Client wants to be a spectator, make sure we are not using any controller
UnregisterNetPlayDevice(this);
_controllerPort = port;
} else {
GameServerConnection* netPlayDevice = GetNetPlayDevice(port);
if(netPlayDevice == this) {
//Nothing to do, we're already this player
} else if(netPlayDevice == nullptr) {
//This port is free, we can switch
UnregisterNetPlayDevice(this);
RegisterNetPlayDevice(this, port);
_controllerPort = port;
} else {
//Another player is using this port, we can't use it
}
}
SendGameInformation();
GameServer::SendPlayerList();
Console::Resume();
}
void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
switch(type) {
@ -138,4 +182,29 @@ void GameServerConnection::UnregisterNetPlayDevice(GameServerConnection* device)
GameServerConnection* GameServerConnection::GetNetPlayDevice(uint8_t port)
{
return GameServerConnection::_netPlayDevices[port];
}
uint8_t GameServerConnection::GetFirstFreeControllerPort()
{
uint8_t hostPost = GameServer::GetHostControllerPort();
for(int i = 0; i < 4; i++) {
if(hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr) {
return i;
}
}
return GameConnection::SpectatorPort;
}
string GameServerConnection::GetPlayerName()
{
if(_connectionData) {
return _connectionData->PlayerName;
} else {
return "";
}
}
uint8_t GameServerConnection::GetControllerPort()
{
return _controllerPort;
}

View file

@ -6,29 +6,39 @@
#include "IGameBroadcaster.h"
#include "INotificationListener.h"
class HandShakeMessage;
class GameServerConnection : public GameConnection, public INotificationListener
{
private:
static GameServerConnection* _netPlayDevices[4];
list<uint32_t> _inputData;
int _controllerPort;
bool _handshakeCompleted = false;
void PushState(uint32_t state);
void SendGameInformation();
void SelectControllerPort(uint8_t port);
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);
public:
GameServerConnection(shared_ptr<Socket> socket, int controllerPort);
GameServerConnection(shared_ptr<Socket> socket);
~GameServerConnection();
uint32_t GetState();
void SendMovieData(uint8_t state, uint8_t port);
string GetPlayerName();
uint8_t GetControllerPort();
virtual void ProcessNotification(ConsoleNotificationType type, void* parameter);
static GameServerConnection* GetNetPlayDevice(uint8_t port);

View file

@ -11,6 +11,7 @@ private:
uint32_t _playerNameLength = 0;
void* _avatarData = nullptr;
uint32_t _avatarSize = 0;
bool _spectator = false;
protected:
virtual void ProtectedStreamState()
@ -18,16 +19,18 @@ protected:
Stream<uint32_t>(_protocolVersion);
StreamArray((void**)&_playerName, _playerNameLength);
StreamArray(&_avatarData, _avatarSize);
Stream<bool>(_spectator);
}
public:
HandShakeMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
HandShakeMessage(string playerName, uint8_t* avatarData, uint32_t avatarSize) : NetMessage(MessageType::HandShake)
HandShakeMessage(string playerName, uint8_t* avatarData, uint32_t avatarSize, bool spectator) : NetMessage(MessageType::HandShake)
{
_protocolVersion = 1;
CopyString(&_playerName, _playerNameLength, playerName);
_avatarSize = avatarSize;
_avatarData = avatarData;
_spectator = spectator;
}
string GetPlayerName()
@ -49,4 +52,9 @@ public:
{
return _protocolVersion == CurrentVersion;
}
bool IsSpectator()
{
return _spectator;
}
};

View file

@ -8,4 +8,6 @@ enum class MessageType : uint8_t
InputData = 2,
MovieData = 3,
GameInformation = 4,
PlayerList = 5,
SelectController = 6,
};

View file

@ -28,6 +28,13 @@ protected:
}
}
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) {

63
Core/PlayerListMessage.h Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
class PlayerListMessage : public NetMessage
{
private:
static const uint32_t PlayerNameMaxLength = 50;
vector<PlayerInfo> _playerList;
protected:
virtual void ProtectedStreamState()
{
uint32_t nameLength = PlayerNameMaxLength + 1;
char playerName[PlayerNameMaxLength + 1];
uint8_t playerPort;
bool isHost;
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;
}
};

View file

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

View file

@ -10,5 +10,6 @@ namespace Mesen.GUI.Config
{
public string Host = "localhost";
public UInt16 Port = 8888;
public bool Spectator = false;
}
}

View file

@ -135,9 +135,10 @@ namespace Mesen.GUI.Config
InteropEmu.SetConsoleType(inputInfo.ConsoleType);
InteropEmu.SetExpansionDevice(inputInfo.ExpansionPortDevice);
InteropEmu.SetFlag(EmulationFlags.HasFourScore, (inputInfo.ConsoleType == ConsoleType.Nes && inputInfo.UseFourScore) || (inputInfo.ConsoleType == ConsoleType.Famicom && expansionDevice == InteropEmu.ExpansionPortDevice.FourPlayerAdapter));
bool hasFourScore = (inputInfo.ConsoleType == ConsoleType.Nes && inputInfo.UseFourScore) || (inputInfo.ConsoleType == ConsoleType.Famicom && expansionDevice == InteropEmu.ExpansionPortDevice.FourPlayerAdapter);
InteropEmu.SetFlag(EmulationFlags.HasFourScore, hasFourScore);
for(int i = 0; i < 4; i++) {
InteropEmu.SetControllerType(i, inputInfo.Controllers[i].ControllerType);
InteropEmu.SetControllerType(i, i < 2 || hasFourScore ? inputInfo.Controllers[i].ControllerType : InteropEmu.ControllerType.None);
InteropEmu.SetControllerKeys(i, inputInfo.Controllers[i].GetKeyMappingSet());
}
}

View file

@ -36,6 +36,11 @@
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 111);
this.baseConfigPanel.Size = new System.Drawing.Size(290, 29);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
@ -54,7 +59,7 @@
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.Size = new System.Drawing.Size(290, 111);
this.tableLayoutPanel1.TabIndex = 0;
//
// txtPort
@ -99,7 +104,6 @@
//
this.chkSpectator.AutoSize = true;
this.tableLayoutPanel1.SetColumnSpan(this.chkSpectator, 2);
this.chkSpectator.Enabled = false;
this.chkSpectator.Location = new System.Drawing.Point(3, 55);
this.chkSpectator.Name = "chkSpectator";
this.chkSpectator.Size = new System.Drawing.Size(106, 17);
@ -119,9 +123,9 @@
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(306, 178);
this.Name = "frmClientConfig";
this.ShowInTaskbar = false;
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();

View file

@ -18,13 +18,16 @@ namespace Mesen.GUI.Forms.NetPlay
{
InitializeComponent();
this.txtHost.Text = ConfigManager.Config.ClientConnectionInfo.Host;
Entity = ConfigManager.Config.ClientConnectionInfo;
AddBinding("Host", this.txtHost);
AddBinding("Spectator", chkSpectator);
this.txtPort.Text = ConfigManager.Config.ClientConnectionInfo.Port.ToString();
}
protected override void UpdateConfig()
{
ConfigManager.Config.ClientConnectionInfo = new ClientConnectionInfo() { Host = this.txtHost.Text, Port = Convert.ToUInt16(this.txtPort.Text) };
((ClientConnectionInfo)Entity).Port = Convert.ToUInt16(this.txtPort.Text);
}
private void Field_TextChanged(object sender, EventArgs e)

View file

@ -117,4 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -91,6 +91,13 @@
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.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator();
this.mnuNetPlaySpectator = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuFindServer = new System.Windows.Forms.ToolStripMenuItem();
this.mnuProfile = new System.Windows.Forms.ToolStripMenuItem();
@ -556,6 +563,7 @@
this.mnuNetPlay.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuStartServer,
this.mnuConnect,
this.mnuNetPlaySelectController,
this.toolStripMenuItem2,
this.mnuFindServer,
this.mnuProfile});
@ -578,6 +586,59 @@
this.mnuConnect.Text = "Connect to Server";
this.mnuConnect.Click += new System.EventHandler(this.mnuConnect_Click);
//
// mnuNetPlaySelectController
//
this.mnuNetPlaySelectController.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuNetPlayPlayer1,
this.mnuNetPlayPlayer2,
this.mnuNetPlayPlayer3,
this.mnuNetPlayPlayer4,
this.toolStripMenuItem3,
this.mnuNetPlaySpectator});
this.mnuNetPlaySelectController.Name = "mnuNetPlaySelectController";
this.mnuNetPlaySelectController.Size = new System.Drawing.Size(177, 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";
this.mnuNetPlayPlayer1.Click += new System.EventHandler(this.mnuNetPlayPlayer1_Click);
//
// mnuNetPlayPlayer2
//
this.mnuNetPlayPlayer2.Name = "mnuNetPlayPlayer2";
this.mnuNetPlayPlayer2.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer2.Text = "Player 2";
this.mnuNetPlayPlayer2.Click += new System.EventHandler(this.mnuNetPlayPlayer2_Click);
//
// mnuNetPlayPlayer3
//
this.mnuNetPlayPlayer3.Name = "mnuNetPlayPlayer3";
this.mnuNetPlayPlayer3.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer3.Text = "Player 3";
this.mnuNetPlayPlayer3.Click += new System.EventHandler(this.mnuNetPlayPlayer3_Click);
//
// mnuNetPlayPlayer4
//
this.mnuNetPlayPlayer4.Name = "mnuNetPlayPlayer4";
this.mnuNetPlayPlayer4.Size = new System.Drawing.Size(152, 22);
this.mnuNetPlayPlayer4.Text = "Player 4";
this.mnuNetPlayPlayer4.Click += new System.EventHandler(this.mnuNetPlayPlayer4_Click);
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.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";
this.mnuNetPlaySpectator.Click += new System.EventHandler(this.mnuNetPlaySpectator_Click);
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
@ -904,6 +965,13 @@
private System.Windows.Forms.ToolStripMenuItem mnuRegionDendy;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
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.ToolStripSeparator toolStripMenuItem3;
private System.Windows.Forms.ToolStripMenuItem mnuNetPlaySpectator;
}
}

View file

@ -334,6 +334,27 @@ namespace Mesen.GUI.Forms
mnuStartServer.Enabled = !isNetPlayClient;
mnuConnect.Enabled = !InteropEmu.IsServerRunning();
mnuNetPlaySelectController.Enabled = isNetPlayClient || InteropEmu.IsServerRunning();
if(mnuNetPlaySelectController.Enabled) {
int availableControllers = InteropEmu.NetPlayGetAvailableControllers();
int currentControllerPort = InteropEmu.NetPlayGetControllerPort();
mnuNetPlayPlayer1.Enabled = (availableControllers & 0x01) == 0x01;
mnuNetPlayPlayer2.Enabled = (availableControllers & 0x02) == 0x02;
mnuNetPlayPlayer3.Enabled = (availableControllers & 0x04) == 0x04;
mnuNetPlayPlayer4.Enabled = (availableControllers & 0x08) == 0x08;
mnuNetPlayPlayer1.Text = "Player 1 (" + InteropEmu.NetPlayGetControllerType(0).ToString() + ")";
mnuNetPlayPlayer2.Text = "Player 2 (" + InteropEmu.NetPlayGetControllerType(1).ToString() + ")";
mnuNetPlayPlayer3.Text = "Player 3 (" + InteropEmu.NetPlayGetControllerType(2).ToString() + ")";
mnuNetPlayPlayer4.Text = "Player 4 (" + InteropEmu.NetPlayGetControllerType(3).ToString() + ")";
mnuNetPlayPlayer1.Checked = (currentControllerPort == 0);
mnuNetPlayPlayer2.Checked = (currentControllerPort == 1);
mnuNetPlayPlayer3.Checked = (currentControllerPort == 2);
mnuNetPlayPlayer4.Checked = (currentControllerPort == 3);
mnuNetPlaySpectator.Checked = (currentControllerPort == 0xFF);
mnuNetPlaySpectator.Enabled = true;
}
mnuStartServer.Text = InteropEmu.IsServerRunning() ? "Stop Server" : "Start Server";
mnuConnect.Text = isNetPlayClient ? "Disconnect" : "Connect to Server";
@ -510,7 +531,7 @@ namespace Mesen.GUI.Forms
} else {
frmServerConfig frm = new frmServerConfig();
if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) {
InteropEmu.StartServer(ConfigManager.Config.ServerInfo.Port);
InteropEmu.StartServer(ConfigManager.Config.ServerInfo.Port, ConfigManager.Config.Profile.PlayerName);
}
}
}
@ -522,7 +543,7 @@ namespace Mesen.GUI.Forms
} else {
frmClientConfig frm = new frmClientConfig();
if(frm.ShowDialog(sender) == System.Windows.Forms.DialogResult.OK) {
InteropEmu.Connect(ConfigManager.Config.ClientConnectionInfo.Host, ConfigManager.Config.ClientConnectionInfo.Port, ConfigManager.Config.Profile.PlayerName, ConfigManager.Config.Profile.PlayerAvatar, (UInt16)ConfigManager.Config.Profile.PlayerAvatar.Length);
InteropEmu.Connect(ConfigManager.Config.ClientConnectionInfo.Host, ConfigManager.Config.ClientConnectionInfo.Port, ConfigManager.Config.Profile.PlayerName, ConfigManager.Config.Profile.PlayerAvatar, (UInt16)ConfigManager.Config.Profile.PlayerAvatar.Length, ConfigManager.Config.ClientConnectionInfo.Spectator);
}
}
}
@ -876,5 +897,30 @@ namespace Mesen.GUI.Forms
e.Effect = DragDropEffects.Copy;
}
}
private void mnuNetPlayPlayer1_Click(object sender, EventArgs e)
{
InteropEmu.NetPlaySelectController(0);
}
private void mnuNetPlayPlayer2_Click(object sender, EventArgs e)
{
InteropEmu.NetPlaySelectController(1);
}
private void mnuNetPlayPlayer3_Click(object sender, EventArgs e)
{
InteropEmu.NetPlaySelectController(2);
}
private void mnuNetPlayPlayer4_Click(object sender, EventArgs e)
{
InteropEmu.NetPlaySelectController(3);
}
private void mnuNetPlaySpectator_Click(object sender, EventArgs e)
{
InteropEmu.NetPlaySelectController(0xFF);
}
}
}

View file

@ -43,13 +43,18 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void Stop();
[DllImport(DLLPath, EntryPoint="GetROMPath")] private static extern IntPtr GetROMPathWrapper();
[DllImport(DLLPath)] public static extern void Reset();
[DllImport(DLLPath)] public static extern void StartServer(UInt16 port);
[DllImport(DLLPath)] public static extern void StartServer(UInt16 port, [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 playerName, byte[] avatarData, UInt32 avatarSize);
[DllImport(DLLPath)] public static extern void Connect([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string host, UInt16 port, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string playerName, byte[] avatarData, UInt32 avatarSize, [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 ControllerType NetPlayGetControllerType(Int32 controllerPort);
[DllImport(DLLPath)] public static extern Int32 NetPlayGetControllerPort();
[DllImport(DLLPath)] public static extern void TakeScreenshot();
[DllImport(DLLPath)] public static extern IntPtr RegisterNotificationCallback(NotificationListener.NotificationCallback callback);

View file

@ -111,25 +111,54 @@ namespace InteropEmu {
DllExport void __stdcall Reset() { Console::Reset(); }
DllExport void __stdcall StartServer(uint16_t port) { GameServer::StartServer(port); }
DllExport void __stdcall StartServer(uint16_t port, char* hostPlayerName) { GameServer::StartServer(port, hostPlayerName); }
DllExport void __stdcall StopServer() { GameServer::StopServer(); }
DllExport bool __stdcall IsServerRunning() { return GameServer::Started(); }
DllExport void __stdcall Connect(char* host, uint16_t port, char* playerName, uint8_t* avatarData, uint32_t avatarSize)
DllExport void __stdcall Connect(char* host, uint16_t port, char* playerName, uint8_t* avatarData, uint32_t avatarSize, bool spectator)
{
shared_ptr<ClientConnectionData> connectionData(new ClientConnectionData(
host,
port,
playerName,
avatarData,
avatarSize
));
avatarSize,
spectator
));
GameClient::Connect(connectionData);
}
DllExport void __stdcall Disconnect() { GameClient::Disconnect(); }
DllExport bool __stdcall IsConnected() { return GameClient::Connected(); }
DllExport ControllerType __stdcall NetPlayGetControllerType(int32_t port) { return EmulationSettings::GetControllerType(port); }
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();
}
}
DllExport void __stdcall Pause()
{