diff --git a/Core/ClientConnectionData.h b/Core/ClientConnectionData.h
index d34ee838..6cd9caec 100644
--- a/Core/ClientConnectionData.h
+++ b/Core/ClientConnectionData.h
@@ -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];
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 50ad943f..20dd7542 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -407,9 +407,11 @@
+
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 6ebbbd91..f0a30f19 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -539,6 +539,12 @@
Nes\Controllers
+
+ NetPlay\Messages
+
+
+ NetPlay\Messages
+
diff --git a/Core/GameClient.cpp b/Core/GameClient.cpp
index e487491c..1ad12521 100644
--- a/Core/GameClient.cpp
+++ b/Core/GameClient.cpp
@@ -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;
}
\ No newline at end of file
diff --git a/Core/GameClient.h b/Core/GameClient.h
index d1b3ccc2..fb2ab698 100644
--- a/Core/GameClient.h
+++ b/Core/GameClient.h
@@ -29,6 +29,10 @@ public:
static void Connect(shared_ptr 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);
diff --git a/Core/GameClientConnection.cpp b/Core/GameClientConnection.cpp
index 3db40125..66819e76 100644
--- a/Core/GameClientConnection.cpp
+++ b/Core/GameClientConnection.cpp
@@ -10,6 +10,8 @@
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "StandardController.h"
+#include "SelectControllerMessage.h"
+#include "PlayerListMessage.h"
GameClientConnection::GameClientConnection(shared_ptr socket, shared_ptr 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;
}
\ No newline at end of file
diff --git a/Core/GameClientConnection.h b/Core/GameClientConnection.h
index 2cbf474a..cfbf8c6e 100644
--- a/Core/GameClientConnection.h
+++ b/Core/GameClientConnection.h
@@ -19,13 +19,16 @@ private:
atomic _enableControllers = false;
atomic _minimumQueueSize = 3;
+ vector _playerList;
+
shared_ptr _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();
};
\ No newline at end of file
diff --git a/Core/GameConnection.cpp b/Core/GameConnection.cpp
index 1c4686c0..1cab2c29 100644
--- a/Core/GameConnection.cpp
+++ b/Core/GameConnection.cpp
@@ -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, shared_ptr 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;
diff --git a/Core/GameConnection.h b/Core/GameConnection.h
index 6bf77045..ba233014 100644
--- a/Core/GameConnection.h
+++ b/Core/GameConnection.h
@@ -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, shared_ptr connectionData);
bool ConnectionError();
-
void ProcessMessages();
-
+ void SendNetMessage(NetMessage &message);
};
\ No newline at end of file
diff --git a/Core/GameServer.cpp b/Core/GameServer.cpp
index 34c902e7..d9f28c07 100644
--- a/Core/GameServer.cpp
+++ b/Core/GameServer.cpp
@@ -6,11 +6,15 @@ using std::thread;
#include "GameServer.h"
#include "Console.h"
#include "../Utilities/Socket.h"
+#include "PlayerListMessage.h"
unique_ptr 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 = _listener->Accept();
if(!socket->ConnectionError()) {
- _openConnections.push_back(shared_ptr(new GameServerConnection(socket, 1)));
+ _openConnections.push_back(shared_ptr(new GameServerConnection(socket)));
} else {
break;
}
@@ -53,6 +57,15 @@ void GameServer::UpdateConnections()
}
}
+list> GameServer::GetConnectionList()
+{
+ if(GameServer::Started()) {
+ return Instance->_openConnections;
+ } else {
+ return list>();
+ }
+}
+
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 GameServer::GetPlayerList()
+{
+ vector playerList;
+
+ PlayerInfo playerInfo;
+ playerInfo.Name = GetHostPlayerName();
+ playerInfo.ControllerPort = GetHostControllerPort();
+ playerInfo.IsHost = true;
+ playerList.push_back(playerInfo);
+
+ for(shared_ptr &connection : GetConnectionList()) {
+ playerInfo.Name = connection->GetPlayerName();
+ playerInfo.ControllerPort = connection->GetControllerPort();
+ playerInfo.IsHost = false;
+ playerList.push_back(playerInfo);
+ }
+
+ return playerList;
+}
+
+void GameServer::SendPlayerList()
+{
+ vector playerList = GetPlayerList();
+
+ for(shared_ptr &connection : GetConnectionList()) {
+ //Send player list update to all connections
+ PlayerListMessage message(playerList);
+ connection->SendNetMessage(message);
+ }
}
\ No newline at end of file
diff --git a/Core/GameServer.h b/Core/GameServer.h
index f046760c..b664127d 100644
--- a/Core/GameServer.h
+++ b/Core/GameServer.h
@@ -18,6 +18,9 @@ private:
list> _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 GetPlayerList();
+ static void SendPlayerList();
+
+ static list> GetConnectionList();
+
virtual void BroadcastInput(uint8_t inputData, uint8_t port);
};
\ No newline at end of file
diff --git a/Core/GameServerConnection.cpp b/Core/GameServerConnection.cpp
index d7a7dde1..847a9a37 100644
--- a/Core/GameServerConnection.cpp
+++ b/Core/GameServerConnection.cpp
@@ -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, int controllerPort) : GameConnection(socket, nullptr)
+GameServerConnection::GameServerConnection(shared_ptr 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;
}
\ No newline at end of file
diff --git a/Core/GameServerConnection.h b/Core/GameServerConnection.h
index 2e6e602a..9e0521d9 100644
--- a/Core/GameServerConnection.h
+++ b/Core/GameServerConnection.h
@@ -6,29 +6,39 @@
#include "IGameBroadcaster.h"
#include "INotificationListener.h"
+class HandShakeMessage;
+
class GameServerConnection : public GameConnection, public INotificationListener
{
private:
static GameServerConnection* _netPlayDevices[4];
+
list _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, int controllerPort);
+ GameServerConnection(shared_ptr 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);
diff --git a/Core/HandShakeMessage.h b/Core/HandShakeMessage.h
index 77629424..f55e4665 100644
--- a/Core/HandShakeMessage.h
+++ b/Core/HandShakeMessage.h
@@ -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(_protocolVersion);
StreamArray((void**)&_playerName, _playerNameLength);
StreamArray(&_avatarData, _avatarSize);
+ Stream(_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;
+ }
};
diff --git a/Core/MessageType.h b/Core/MessageType.h
index b37f64f4..99d03822 100644
--- a/Core/MessageType.h
+++ b/Core/MessageType.h
@@ -8,4 +8,6 @@ enum class MessageType : uint8_t
InputData = 2,
MovieData = 3,
GameInformation = 4,
+ PlayerList = 5,
+ SelectController = 6,
};
\ No newline at end of file
diff --git a/Core/NetMessage.h b/Core/NetMessage.h
index 9b1c1f14..50f4e3c1 100644
--- a/Core/NetMessage.h
+++ b/Core/NetMessage.h
@@ -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) {
diff --git a/Core/PlayerListMessage.h b/Core/PlayerListMessage.h
new file mode 100644
index 00000000..cc373c7e
--- /dev/null
+++ b/Core/PlayerListMessage.h
@@ -0,0 +1,63 @@
+#pragma once
+#include "stdafx.h"
+#include "NetMessage.h"
+
+class PlayerListMessage : public NetMessage
+{
+private:
+ static const uint32_t PlayerNameMaxLength = 50;
+ vector _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(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(playerPort);
+ Stream(isHost);
+ }
+ } else {
+ uint32_t playerCount;
+ Stream(playerCount);
+
+ for(uint32_t i = 0; i < playerCount; i++) {
+ memset(playerName, 0, nameLength);
+ StreamArray(playerName, nameLength);
+ Stream(playerPort);
+ Stream(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 playerList) : NetMessage(MessageType::PlayerList)
+ {
+ _playerList = playerList;
+ }
+
+ vector GetPlayerList()
+ {
+ return _playerList;
+ }
+};
\ No newline at end of file
diff --git a/Core/SelectControllerMessage.h b/Core/SelectControllerMessage.h
new file mode 100644
index 00000000..ff22695b
--- /dev/null
+++ b/Core/SelectControllerMessage.h
@@ -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(_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;
+ }
+};
\ No newline at end of file
diff --git a/GUI.NET/Config/ClientConnection.cs b/GUI.NET/Config/ClientConnection.cs
index 64360a75..bd7f9887 100644
--- a/GUI.NET/Config/ClientConnection.cs
+++ b/GUI.NET/Config/ClientConnection.cs
@@ -10,5 +10,6 @@ namespace Mesen.GUI.Config
{
public string Host = "localhost";
public UInt16 Port = 8888;
+ public bool Spectator = false;
}
}
diff --git a/GUI.NET/Config/InputInfo.cs b/GUI.NET/Config/InputInfo.cs
index 0b756971..a1a3213e 100644
--- a/GUI.NET/Config/InputInfo.cs
+++ b/GUI.NET/Config/InputInfo.cs
@@ -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());
}
}
diff --git a/GUI.NET/Forms/NetPlay/frmClientConfig.Designer.cs b/GUI.NET/Forms/NetPlay/frmClientConfig.Designer.cs
index e8256506..63bdbb33 100644
--- a/GUI.NET/Forms/NetPlay/frmClientConfig.Designer.cs
+++ b/GUI.NET/Forms/NetPlay/frmClientConfig.Designer.cs
@@ -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();
diff --git a/GUI.NET/Forms/NetPlay/frmClientConfig.cs b/GUI.NET/Forms/NetPlay/frmClientConfig.cs
index 63be871e..1c626536 100644
--- a/GUI.NET/Forms/NetPlay/frmClientConfig.cs
+++ b/GUI.NET/Forms/NetPlay/frmClientConfig.cs
@@ -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)
diff --git a/GUI.NET/Forms/NetPlay/frmClientConfig.resx b/GUI.NET/Forms/NetPlay/frmClientConfig.resx
index 1af7de15..8766f298 100644
--- a/GUI.NET/Forms/NetPlay/frmClientConfig.resx
+++ b/GUI.NET/Forms/NetPlay/frmClientConfig.resx
@@ -117,4 +117,7 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ 17, 17
+
\ No newline at end of file
diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs
index bca41c73..b7af43a5 100644
--- a/GUI.NET/Forms/frmMain.Designer.cs
+++ b/GUI.NET/Forms/frmMain.Designer.cs
@@ -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;
}
}
diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs
index 01f74c39..d5afd121 100644
--- a/GUI.NET/Forms/frmMain.cs
+++ b/GUI.NET/Forms/frmMain.cs
@@ -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);
+ }
}
}
diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs
index 28970883..f8b2a1bc 100644
--- a/GUI.NET/InteropEmu.cs
+++ b/GUI.NET/InteropEmu.cs
@@ -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);
diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp
index c9649eb9..1d9ab377 100644
--- a/InteropDLL/ConsoleWrapper.cpp
+++ b/InteropDLL/ConsoleWrapper.cpp
@@ -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 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()
{