Netplay (not finished, but works), save state fixes
This commit is contained in:
parent
021efccbed
commit
f4802421d0
30 changed files with 979 additions and 79 deletions
|
@ -78,9 +78,8 @@ bool APU::Exec(uint32_t executedCycles)
|
|||
if(availableSampleCount >= APU::SamplesPerFrame) {
|
||||
size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame);
|
||||
APU::AudioDevice->PlayBuffer(_outputBuffer, sampleCount * BitsPerSample / 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -91,6 +90,8 @@ void APU::StreamState(bool saving)
|
|||
if(saving) {
|
||||
_apu.save_snapshot(&snapshot);
|
||||
}
|
||||
|
||||
Stream<uint32_t>(_currentClock);
|
||||
|
||||
StreamArray<uint8_t>(snapshot.w40xx, 0x14);
|
||||
Stream<uint8_t>(snapshot.w4015);
|
||||
|
|
127
Core/Console.cpp
127
Core/Console.cpp
|
@ -4,11 +4,11 @@
|
|||
#include "../Utilities/Timer.h"
|
||||
|
||||
Console* Console::Instance = nullptr;
|
||||
IMessageManager* Console::MessageManager = nullptr;
|
||||
uint32_t Console::Flags = 0;
|
||||
uint32_t Console::CurrentFPS = 0;
|
||||
atomic_flag Console::PauseFlag;
|
||||
atomic_flag Console::RunningFlag;
|
||||
|
||||
SimpleLock Console::PauseLock;
|
||||
SimpleLock Console::RunningLock;
|
||||
|
||||
Console::Console(wstring filename)
|
||||
{
|
||||
|
@ -29,16 +29,16 @@ Console::Console(wstring filename)
|
|||
|
||||
ResetComponents(false);
|
||||
|
||||
Console::PauseFlag.clear();
|
||||
Console::RunningFlag.clear();
|
||||
Console::PauseLock.Release();
|
||||
Console::RunningLock.Release();
|
||||
Console::Instance = this;
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
{
|
||||
Movie::Stop();
|
||||
Console::PauseFlag.clear();
|
||||
Console::RunningFlag.clear();
|
||||
Console::PauseLock.Release();
|
||||
Console::RunningLock.Release();
|
||||
if(Console::Instance == this) {
|
||||
Console::Instance = nullptr;
|
||||
}
|
||||
|
@ -66,16 +66,16 @@ void Console::Stop()
|
|||
|
||||
void Console::Pause()
|
||||
{
|
||||
while(Console::PauseFlag.test_and_set());
|
||||
Console::PauseLock.Acquire();
|
||||
|
||||
//Spin wait until emu pauses
|
||||
while(Console::RunningFlag.test_and_set());
|
||||
Console::RunningLock.Acquire();
|
||||
}
|
||||
|
||||
void Console::Resume()
|
||||
{
|
||||
Console::RunningFlag.clear();
|
||||
Console::PauseFlag.clear();
|
||||
Console::RunningLock.Release();
|
||||
Console::PauseLock.Release();
|
||||
}
|
||||
|
||||
void Console::SetFlags(int flags)
|
||||
|
@ -93,6 +93,18 @@ bool Console::CheckFlag(int flag)
|
|||
return (Console::Flags & flag) == flag;
|
||||
}
|
||||
|
||||
void Console::RegisterMessageManager(IMessageManager* messageManager)
|
||||
{
|
||||
Console::MessageManager = messageManager;
|
||||
}
|
||||
|
||||
void Console::DisplayMessage(wstring message)
|
||||
{
|
||||
if(Console::MessageManager) {
|
||||
Console::MessageManager->DisplayMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Console::GetFPS()
|
||||
{
|
||||
return Console::CurrentFPS;
|
||||
|
@ -105,35 +117,18 @@ void Console::Run()
|
|||
uint32_t lastFrameCount = 0;
|
||||
double elapsedTime = 0;
|
||||
double targetTime = 16.6666666666666666;
|
||||
while(Console::RunningFlag.test_and_set());
|
||||
|
||||
Console::RunningLock.Acquire();
|
||||
|
||||
while(true) {
|
||||
uint32_t executedCycles = _cpu->Exec();
|
||||
_ppu->Exec();
|
||||
|
||||
bool frameDone = _apu->Exec(executedCycles);
|
||||
|
||||
if(frameDone) {
|
||||
_cpu->EndFrame();
|
||||
_ppu->EndFrame();
|
||||
|
||||
if(Console::PauseFlag.test_and_set()) {
|
||||
//Need to temporarely pause the emu (to save/load a state, etc.)
|
||||
Console::RunningFlag.clear();
|
||||
|
||||
//Spin wait until we are allowed to start again
|
||||
while(Console::PauseFlag.test_and_set());
|
||||
Console::PauseFlag.clear();
|
||||
|
||||
while(Console::RunningFlag.test_and_set());
|
||||
} else {
|
||||
Console::PauseFlag.clear();
|
||||
}
|
||||
|
||||
if(_stop) {
|
||||
_stop = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) {
|
||||
elapsedTime = clockTimer.GetElapsedMS();
|
||||
while(targetTime > elapsedTime) {
|
||||
|
@ -142,18 +137,34 @@ void Console::Run()
|
|||
}
|
||||
elapsedTime = clockTimer.GetElapsedMS();
|
||||
}
|
||||
clockTimer.Reset();
|
||||
}
|
||||
|
||||
if(!Console::PauseLock.IsFree()) {
|
||||
//Need to temporarely pause the emu (to save/load a state, etc.)
|
||||
Console::RunningLock.Release();
|
||||
|
||||
//Spin wait until we are allowed to start again
|
||||
Console::PauseLock.WaitForRelease();
|
||||
|
||||
Console::RunningLock.Acquire();
|
||||
}
|
||||
|
||||
if(fpsTimer.GetElapsedMS() > 1000) {
|
||||
uint32_t frameCount = _ppu->GetFrameCount();
|
||||
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)));
|
||||
lastFrameCount = frameCount;
|
||||
fpsTimer.Reset();
|
||||
clockTimer.Reset();
|
||||
|
||||
if(_stop) {
|
||||
_stop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(fpsTimer.GetElapsedMS() > 1000) {
|
||||
uint32_t frameCount = _ppu->GetFrameCount();
|
||||
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)));
|
||||
lastFrameCount = frameCount;
|
||||
fpsTimer.Reset();
|
||||
}
|
||||
}
|
||||
Console::RunningFlag.clear();
|
||||
Console::RunningLock.Release();
|
||||
}
|
||||
|
||||
void Console::SaveState(wstring filename)
|
||||
|
@ -165,6 +176,8 @@ void Console::SaveState(wstring filename)
|
|||
Console::SaveState(file);
|
||||
Console::Resume();
|
||||
file.close();
|
||||
|
||||
Console::DisplayMessage(L"State saved.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,27 +190,45 @@ bool Console::LoadState(wstring filename)
|
|||
Console::LoadState(file);
|
||||
Console::Resume();
|
||||
file.close();
|
||||
|
||||
Console::DisplayMessage(L"State loaded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Console::DisplayMessage(L"Slot is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Console::SaveState(ostream &saveStream)
|
||||
{
|
||||
Instance->_cpu->SaveSnapshot(&saveStream);
|
||||
Instance->_ppu->SaveSnapshot(&saveStream);
|
||||
Instance->_memoryManager->SaveSnapshot(&saveStream);
|
||||
Instance->_mapper->SaveSnapshot(&saveStream);
|
||||
Instance->_apu->SaveSnapshot(&saveStream);
|
||||
if(Instance) {
|
||||
Instance->_cpu->SaveSnapshot(&saveStream);
|
||||
Instance->_ppu->SaveSnapshot(&saveStream);
|
||||
Instance->_memoryManager->SaveSnapshot(&saveStream);
|
||||
Instance->_mapper->SaveSnapshot(&saveStream);
|
||||
Instance->_apu->SaveSnapshot(&saveStream);
|
||||
Instance->_controlManager->SaveSnapshot(&saveStream);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::LoadState(istream &loadStream)
|
||||
{
|
||||
Instance->_cpu->LoadSnapshot(&loadStream);
|
||||
Instance->_ppu->LoadSnapshot(&loadStream);
|
||||
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
||||
Instance->_mapper->LoadSnapshot(&loadStream);
|
||||
Instance->_apu->LoadSnapshot(&loadStream);
|
||||
if(Instance) {
|
||||
Instance->_cpu->LoadSnapshot(&loadStream);
|
||||
Instance->_ppu->LoadSnapshot(&loadStream);
|
||||
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
||||
Instance->_mapper->LoadSnapshot(&loadStream);
|
||||
Instance->_apu->LoadSnapshot(&loadStream);
|
||||
Instance->_controlManager->LoadSnapshot(&loadStream);
|
||||
}
|
||||
}
|
||||
|
||||
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
||||
{
|
||||
stringstream stream;
|
||||
stream.write((char*)buffer, bufferSize);
|
||||
stream.seekg(0, ios::beg);
|
||||
LoadState(stream);
|
||||
}
|
||||
|
||||
bool Console::RunTest(uint8_t *expectedResult)
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "BaseMapper.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "ControlManager.h"
|
||||
#include "../Utilities/SimpleLock.h"
|
||||
#include "IMessageManager.h"
|
||||
|
||||
enum EmulationFlags
|
||||
{
|
||||
|
@ -19,8 +21,9 @@ class Console
|
|||
static Console* Instance;
|
||||
static uint32_t Flags;
|
||||
static uint32_t CurrentFPS;
|
||||
static atomic_flag PauseFlag;
|
||||
static atomic_flag RunningFlag;
|
||||
static SimpleLock PauseLock;
|
||||
static SimpleLock RunningLock;
|
||||
static IMessageManager* MessageManager;
|
||||
|
||||
unique_ptr<CPU> _cpu;
|
||||
unique_ptr<PPU> _ppu;
|
||||
|
@ -56,9 +59,13 @@ class Console
|
|||
static void SaveState(ostream &saveStream);
|
||||
static bool LoadState(wstring filename);
|
||||
static void LoadState(istream &loadStream);
|
||||
static void LoadState(uint8_t *buffer, uint32_t bufferSize);
|
||||
|
||||
static bool CheckFlag(int flag);
|
||||
static void SetFlags(int flags);
|
||||
static void ClearFlags(int flags);
|
||||
static uint32_t GetFPS();
|
||||
|
||||
static void RegisterMessageManager(IMessageManager* messageManager);
|
||||
static void DisplayMessage(wstring message);
|
||||
};
|
||||
|
|
|
@ -2,17 +2,60 @@
|
|||
#include "ControlManager.h"
|
||||
|
||||
IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
|
||||
IControlDevice* ControlManager::OriginalControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
|
||||
IGameBroadcaster* ControlManager::GameBroadcaster = nullptr;
|
||||
|
||||
ControlManager::ControlManager()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ControlManager::RegisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||
{
|
||||
ControlManager::GameBroadcaster = gameBroadcaster;
|
||||
}
|
||||
|
||||
void ControlManager::UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster)
|
||||
{
|
||||
if(ControlManager::GameBroadcaster == gameBroadcaster) {
|
||||
ControlManager::GameBroadcaster = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::BackupControlDevices()
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
OriginalControlDevices[i] = ControlDevices[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::RestoreControlDevices()
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
ControlDevices[i] = OriginalControlDevices[i];
|
||||
}
|
||||
}
|
||||
|
||||
IControlDevice* ControlManager::GetControlDevice(uint8_t port)
|
||||
{
|
||||
return ControlManager::ControlDevices[port];
|
||||
}
|
||||
|
||||
void ControlManager::RegisterControlDevice(IControlDevice* controlDevice, uint8_t port)
|
||||
{
|
||||
ControlManager::ControlDevices[port] = controlDevice;
|
||||
}
|
||||
|
||||
void ControlManager::UnregisterControlDevice(IControlDevice* controlDevice)
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(ControlManager::ControlDevices[i] == controlDevice) {
|
||||
ControlManager::ControlDevices[i] = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ControlManager::RefreshAllPorts()
|
||||
{
|
||||
RefreshStateBuffer(0);
|
||||
|
@ -34,11 +77,7 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
|
|||
state = Movie::Instance->GetState(port);
|
||||
} else {
|
||||
if(controlDevice) {
|
||||
ButtonState buttonState = controlDevice->GetButtonState();
|
||||
|
||||
//"Button status for each controller is returned as an 8-bit report in the following order: A, B, Select, Start, Up, Down, Left, Right."
|
||||
state = (uint8_t)buttonState.A | ((uint8_t)buttonState.B << 1) | ((uint8_t)buttonState.Select << 2) | ((uint8_t)buttonState.Start << 3) |
|
||||
((uint8_t)buttonState.Up << 4) | ((uint8_t)buttonState.Down << 5) | ((uint8_t)buttonState.Left << 6) | ((uint8_t)buttonState.Right << 7);
|
||||
state = controlDevice->GetButtonState().ToByte();
|
||||
} else {
|
||||
state = 0x00;
|
||||
}
|
||||
|
@ -46,6 +85,9 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
|
|||
|
||||
//Used when recording movies
|
||||
Movie::Instance->RecordState(port, state);
|
||||
if(ControlManager::GameBroadcaster) {
|
||||
ControlManager::GameBroadcaster->BroadcastInput(state, port);
|
||||
}
|
||||
|
||||
_stateBuffer[port] = state;
|
||||
}
|
||||
|
@ -96,4 +138,10 @@ void ControlManager::WriteRAM(uint16_t addr, uint8_t value)
|
|||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ControlManager::StreamState(bool saving)
|
||||
{
|
||||
StreamArray<uint8_t>(_stateBuffer, 4);
|
||||
Stream<bool>(_refreshState);
|
||||
}
|
|
@ -4,11 +4,16 @@
|
|||
#include "IControlDevice.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "Movie.h"
|
||||
#include "IGameBroadcaster.h"
|
||||
#include "Snapshotable.h"
|
||||
|
||||
class ControlManager : public IMemoryHandler
|
||||
class ControlManager : public Snapshotable, public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
static IControlDevice* ControlDevices[4];
|
||||
static IControlDevice* OriginalControlDevices[4];
|
||||
static IGameBroadcaster* GameBroadcaster;
|
||||
|
||||
bool _refreshState = false;
|
||||
uint8_t _stateBuffer[4];
|
||||
|
||||
|
@ -16,10 +21,20 @@ class ControlManager : public IMemoryHandler
|
|||
void RefreshStateBuffer(uint8_t port);
|
||||
uint8_t GetPortValue(uint8_t port);
|
||||
|
||||
protected:
|
||||
virtual void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
ControlManager();
|
||||
|
||||
static void RegisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||
static void UnregisterBroadcaster(IGameBroadcaster* gameBroadcaster);
|
||||
|
||||
static void BackupControlDevices();
|
||||
static void RestoreControlDevices();
|
||||
static IControlDevice* GetControlDevice(uint8_t port);
|
||||
static void RegisterControlDevice(IControlDevice* controlDevice, uint8_t port);
|
||||
static void UnregisterControlDevice(IControlDevice* controlDevice);
|
||||
|
||||
void GetMemoryRanges(MemoryRanges &ranges)
|
||||
{
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
|
@ -93,12 +95,20 @@
|
|||
<ClInclude Include="CNROM.h" />
|
||||
<ClInclude Include="ColorDreams.h" />
|
||||
<ClInclude Include="ControlManager.h" />
|
||||
<ClInclude Include="GameClient.h" />
|
||||
<ClInclude Include="GameClientConnection.h" />
|
||||
<ClInclude Include="GameConnection.h" />
|
||||
<ClInclude Include="GameServer.h" />
|
||||
<ClInclude Include="GameServerConnection.h" />
|
||||
<ClInclude Include="IAudioDevice.h" />
|
||||
<ClInclude Include="IControlDevice.h" />
|
||||
<ClInclude Include="IGameBroadcaster.h" />
|
||||
<ClInclude Include="IMemoryHandler.h" />
|
||||
<ClInclude Include="Console.h" />
|
||||
<ClInclude Include="IMessageManager.h" />
|
||||
<ClInclude Include="MMC2.h" />
|
||||
<ClInclude Include="Movie.h" />
|
||||
<ClInclude Include="NetMessage.h" />
|
||||
<ClInclude Include="Snapshotable.h" />
|
||||
<ClInclude Include="IVideoDevice.h" />
|
||||
<ClInclude Include="MapperFactory.h" />
|
||||
|
@ -123,6 +133,7 @@
|
|||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="UNROM.h" />
|
||||
<ClInclude Include="VirtualController.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="APU.cpp" />
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
<Filter Include="Header Files\Mappers">
|
||||
<UniqueIdentifier>{67b52e86-0ff2-4dbe-b9ed-7c92aace61d5}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\NetPlay">
|
||||
<UniqueIdentifier>{43f375c6-d7c7-4bbf-8520-ad53e4e1b4a3}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
|
@ -132,6 +135,33 @@
|
|||
<ClInclude Include="Movie.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameConnection.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameClient.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameServer.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VirtualController.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NetMessage.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IGameBroadcaster.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameClientConnection.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameServerConnection.h">
|
||||
<Filter>Header Files\NetPlay</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IMessageManager.h">
|
||||
<Filter>Header Files\Interfaces</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CPU.cpp">
|
||||
|
|
62
Core/GameClient.h
Normal file
62
Core/GameClient.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "GameClientConnection.h"
|
||||
|
||||
class GameClient
|
||||
{
|
||||
private:
|
||||
shared_ptr<Socket> _socket;
|
||||
unique_ptr<GameClientConnection> _connection;
|
||||
bool _connected = false;
|
||||
|
||||
public:
|
||||
GameClient()
|
||||
{
|
||||
}
|
||||
|
||||
~GameClient()
|
||||
{
|
||||
}
|
||||
|
||||
bool Connected()
|
||||
{
|
||||
return _connected;
|
||||
}
|
||||
|
||||
void Connect(const char *host, u_short port)
|
||||
{
|
||||
_socket.reset(new Socket());
|
||||
if(_socket->Connect(host, port)) {
|
||||
_connection.reset(new GameClientConnection(_socket));
|
||||
_connected = true;
|
||||
} else {
|
||||
Console::DisplayMessage(L"Could not connect to server.");
|
||||
_connected = false;
|
||||
_socket.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
if(_connected) {
|
||||
_connected = false;
|
||||
_socket.reset();
|
||||
_connection.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Exec()
|
||||
{
|
||||
if(_connected) {
|
||||
if(!_connection->ConnectionError()) {
|
||||
_connection->ProcessMessages();
|
||||
_connection->SendInput();
|
||||
} else {
|
||||
_connected = false;
|
||||
_socket.reset();
|
||||
_connection.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
91
Core/GameServer.h
Normal file
91
Core/GameServer.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "GameServerConnection.h"
|
||||
|
||||
class GameServer : public IGameBroadcaster
|
||||
{
|
||||
private:
|
||||
shared_ptr<Socket> _listener;
|
||||
list<shared_ptr<GameServerConnection>> _openConnections;
|
||||
bool _initialized = false;
|
||||
|
||||
void AcceptConnections()
|
||||
{
|
||||
while(true) {
|
||||
shared_ptr<Socket> socket = _listener->Accept(NULL, NULL);
|
||||
if(!socket->ConnectionError()) {
|
||||
_openConnections.push_back(shared_ptr<GameServerConnection>(new GameServerConnection(socket, 1, this)));
|
||||
std::cout << "Client connected." << std::endl;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_listener->Listen(10);
|
||||
}
|
||||
|
||||
void 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);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GameServer()
|
||||
{
|
||||
ControlManager::RegisterBroadcaster(this);
|
||||
}
|
||||
|
||||
~GameServer()
|
||||
{
|
||||
ControlManager::UnregisterBroadcaster(this);
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
Console::DisplayMessage(L"Server started.");
|
||||
_listener.reset(new Socket());
|
||||
_listener->Bind(8888);
|
||||
_listener->Listen(10);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void Exec()
|
||||
{
|
||||
if(_initialized) {
|
||||
AcceptConnections();
|
||||
UpdateConnections();
|
||||
}
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
_initialized = false;
|
||||
_listener.reset();
|
||||
Console::DisplayMessage(L"Server stopped.");
|
||||
}
|
||||
|
||||
bool Started()
|
||||
{
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
void BroadcastInput(uint8_t inputData, uint8_t port)
|
||||
{
|
||||
for(shared_ptr<GameServerConnection> connection : _openConnections) {
|
||||
if(!connection->ConnectionError()) {
|
||||
//Send movie stream
|
||||
connection->SendMovieData(inputData, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
95
Core/GameServerConnection.h
Normal file
95
Core/GameServerConnection.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "GameConnection.h"
|
||||
#include "IControlDevice.h"
|
||||
#include "IGameBroadcaster.h"
|
||||
|
||||
class GameServerConnection : public GameConnection, public IControlDevice
|
||||
{
|
||||
private:
|
||||
int _controllerPort;
|
||||
list<uint8_t> _inputData;
|
||||
IGameBroadcaster* _gameBroadcaster;
|
||||
bool _handshakeCompleted = false;
|
||||
|
||||
private:
|
||||
void SendGameState()
|
||||
{
|
||||
Console::Pause();
|
||||
stringstream state;
|
||||
Console::SaveState(state);
|
||||
_handshakeCompleted = true;
|
||||
ControlManager::RegisterControlDevice(this, _controllerPort);
|
||||
Console::Resume();
|
||||
|
||||
uint32_t size = (uint32_t)state.tellp();
|
||||
|
||||
char* buffer = new char[size];
|
||||
state.read(buffer, size);
|
||||
SendNetMessage(SaveStateMessage(buffer, size));
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
protected:
|
||||
void ProcessMessage(NetMessage* message)
|
||||
{
|
||||
switch(message->Type) {
|
||||
case MessageType::HandShake:
|
||||
//Send the game's current state to the client and register the controller
|
||||
if(((HandShakeMessage*)message)->IsValid()) {
|
||||
//SendPlayerNumber();
|
||||
SendGameState();
|
||||
}
|
||||
break;
|
||||
case MessageType::InputData:
|
||||
uint8_t state = ((InputDataMessage*)message)->InputState;
|
||||
if(_inputData.size() == 0 || state != _inputData.back()) {
|
||||
_inputData.push_back(state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GameServerConnection(shared_ptr<Socket> socket, int controllerPort, IGameBroadcaster* gameBroadcaster) : GameConnection(socket)
|
||||
{
|
||||
//Server-side connection
|
||||
_gameBroadcaster = gameBroadcaster;
|
||||
|
||||
_controllerPort = controllerPort;
|
||||
|
||||
Console::DisplayMessage(L"Player " + std::to_wstring(_controllerPort) + L" connected.");
|
||||
|
||||
ControlManager::BackupControlDevices();
|
||||
}
|
||||
|
||||
~GameServerConnection()
|
||||
{
|
||||
Console::DisplayMessage(L"Player " + std::to_wstring(_controllerPort) + L" disconnected.");
|
||||
|
||||
ControlManager::RestoreControlDevices();
|
||||
}
|
||||
|
||||
void SendMovieData(uint8_t state, uint8_t port)
|
||||
{
|
||||
if(_handshakeCompleted) {
|
||||
SendNetMessage(MovieDataMessage(state, port));
|
||||
}
|
||||
}
|
||||
|
||||
ButtonState GetButtonState()
|
||||
{
|
||||
ButtonState state;
|
||||
uint32_t inputBufferSize = _inputData.size();
|
||||
uint8_t stateData = 0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
state.FromByte(stateData);
|
||||
return state;
|
||||
}
|
||||
};
|
|
@ -14,6 +14,25 @@ struct ButtonState
|
|||
|
||||
bool Select = false;
|
||||
bool Start = false;
|
||||
|
||||
uint8_t ToByte()
|
||||
{
|
||||
//"Button status for each controller is returned as an 8-bit report in the following order: A, B, Select, Start, Up, Down, Left, Right."
|
||||
return (uint8_t)A | ((uint8_t)B << 1) | ((uint8_t)Select << 2) | ((uint8_t)Start << 3) |
|
||||
((uint8_t)Up << 4) | ((uint8_t)Down << 5) | ((uint8_t)Left << 6) | ((uint8_t)Right << 7);
|
||||
}
|
||||
|
||||
void FromByte(uint8_t stateData)
|
||||
{
|
||||
A = (stateData & 0x01) == 0x01;
|
||||
B = (stateData & 0x02) == 0x02;
|
||||
Select = (stateData & 0x04) == 0x04;
|
||||
Start = (stateData & 0x08) == 0x08;
|
||||
Up = (stateData & 0x10) == 0x10;
|
||||
Down = (stateData & 0x20) == 0x20;
|
||||
Left = (stateData & 0x40) == 0x40;
|
||||
Right = (stateData & 0x80) == 0x80;
|
||||
}
|
||||
};
|
||||
|
||||
class IControlDevice
|
||||
|
|
9
Core/IGameBroadcaster.h
Normal file
9
Core/IGameBroadcaster.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class IGameBroadcaster
|
||||
{
|
||||
public:
|
||||
virtual void BroadcastInput(uint8_t inputData, uint8_t port) = 0;
|
||||
};
|
9
Core/IMessageManager.h
Normal file
9
Core/IMessageManager.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class IMessageManager
|
||||
{
|
||||
public:
|
||||
virtual void DisplayMessage(wstring message) = 0;
|
||||
};
|
|
@ -43,6 +43,7 @@ uint8_t Movie::GetState(uint8_t port)
|
|||
|
||||
if(_readPosition[port] >= _data.DataSize[port]) {
|
||||
//End of movie file
|
||||
Console::DisplayMessage(L"Movie ended.");
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
|
@ -82,6 +83,8 @@ void Movie::StartRecording(wstring filename, bool reset)
|
|||
_recording = true;
|
||||
|
||||
Console::Resume();
|
||||
|
||||
Console::DisplayMessage(L"Recording...");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +116,7 @@ void Movie::PlayMovie(wstring filename)
|
|||
}
|
||||
_playing = true;
|
||||
Console::Resume();
|
||||
Console::DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
150
Core/NetMessage.h
Normal file
150
Core/NetMessage.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "Console.h"
|
||||
|
||||
enum class MessageType : uint8_t
|
||||
{
|
||||
HandShake = 0,
|
||||
SaveState = 1,
|
||||
InputData = 2,
|
||||
MovieData = 3,
|
||||
};
|
||||
|
||||
class NetMessage
|
||||
{
|
||||
public:
|
||||
MessageType Type;
|
||||
|
||||
NetMessage(MessageType type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
virtual void Send(Socket &socket) = 0;
|
||||
};
|
||||
|
||||
class HandShakeMessage : public NetMessage
|
||||
{
|
||||
private:
|
||||
const int CurrentVersion = 1;
|
||||
|
||||
public:
|
||||
uint32_t ProtocolVersion;
|
||||
|
||||
HandShakeMessage(char *readBuffer) : NetMessage(MessageType::HandShake)
|
||||
{
|
||||
ProtocolVersion = *(uint32_t*)readBuffer;
|
||||
}
|
||||
|
||||
HandShakeMessage() : NetMessage(MessageType::HandShake)
|
||||
{
|
||||
ProtocolVersion = 1;
|
||||
}
|
||||
|
||||
bool IsValid()
|
||||
{
|
||||
return ProtocolVersion == CurrentVersion;
|
||||
}
|
||||
|
||||
virtual void Send(Socket &socket)
|
||||
{
|
||||
uint32_t messageLength = sizeof(Type) + sizeof(ProtocolVersion);
|
||||
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||
socket.BufferedSend((char*)&ProtocolVersion, sizeof(ProtocolVersion));
|
||||
socket.SendBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
class InputDataMessage : public NetMessage
|
||||
{
|
||||
public:
|
||||
uint8_t InputState;
|
||||
|
||||
InputDataMessage(char *readBuffer) : NetMessage(MessageType::InputData)
|
||||
{
|
||||
InputState = readBuffer[0];
|
||||
}
|
||||
|
||||
InputDataMessage(uint8_t inputState) : NetMessage(MessageType::InputData)
|
||||
{
|
||||
InputState = inputState;
|
||||
}
|
||||
|
||||
virtual void Send(Socket &socket)
|
||||
{
|
||||
uint32_t messageLength = sizeof(Type) + sizeof(InputState);
|
||||
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||
socket.BufferedSend((char*)&InputState, sizeof(InputState));
|
||||
socket.SendBuffer();
|
||||
}
|
||||
};
|
||||
|
||||
class SaveStateMessage : public NetMessage
|
||||
{
|
||||
private:
|
||||
char* _stateData;
|
||||
uint32_t _dataSize;
|
||||
|
||||
public:
|
||||
SaveStateMessage(char *readBuffer, uint32_t bufferLength) : NetMessage(MessageType::SaveState)
|
||||
{
|
||||
_stateData = new char[bufferLength];
|
||||
_dataSize = bufferLength;
|
||||
memcpy(_stateData, readBuffer, bufferLength);
|
||||
}
|
||||
|
||||
~SaveStateMessage()
|
||||
{
|
||||
delete[] _stateData;
|
||||
}
|
||||
|
||||
virtual void Send(Socket &socket)
|
||||
{
|
||||
uint32_t messageLength = _dataSize + sizeof(Type);
|
||||
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||
socket.BufferedSend((char*)&Type, sizeof(Type));
|
||||
socket.BufferedSend(_stateData, _dataSize);
|
||||
socket.SendBuffer();
|
||||
}
|
||||
|
||||
void LoadState()
|
||||
{
|
||||
Console::LoadState((uint8_t*)_stateData, _dataSize);
|
||||
}
|
||||
};
|
||||
|
||||
class MovieDataMessage : public NetMessage
|
||||
{
|
||||
public:
|
||||
uint8_t PortNumber;
|
||||
uint8_t InputState;
|
||||
|
||||
MovieDataMessage(char *readBuffer) : NetMessage(MessageType::MovieData)
|
||||
{
|
||||
PortNumber = readBuffer[0];
|
||||
InputState = readBuffer[1];
|
||||
}
|
||||
|
||||
MovieDataMessage(uint8_t state, uint8_t port) : NetMessage(MessageType::MovieData)
|
||||
{
|
||||
PortNumber = port;
|
||||
InputState = state;
|
||||
}
|
||||
|
||||
virtual void Send(Socket &socket)
|
||||
{
|
||||
uint32_t messageLength = sizeof(Type) + sizeof(PortNumber) + sizeof(InputState);
|
||||
uint8_t type = (uint8_t)Type;
|
||||
uint8_t portNumber = PortNumber;
|
||||
uint8_t inputState = InputState;
|
||||
|
||||
socket.BufferedSend((char*)&messageLength, sizeof(messageLength));
|
||||
socket.BufferedSend((char*)&type, sizeof(type));
|
||||
socket.BufferedSend((char*)&portNumber, sizeof(portNumber));
|
||||
socket.BufferedSend((char*)&inputState, sizeof(inputState));
|
||||
socket.SendBuffer();
|
||||
}
|
||||
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
#include <array>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
|
@ -32,4 +33,6 @@ using std::ofstream;
|
|||
using std::wstring;
|
||||
using std::exception;
|
||||
using std::atomic_flag;
|
||||
using std::atomic;
|
||||
using std::list;
|
||||
using std::max;
|
BIN
GUI/GUI.rc
BIN
GUI/GUI.rc
Binary file not shown.
|
@ -20,6 +20,8 @@ namespace NES {
|
|||
_renderer.reset(new Renderer(_hWnd));
|
||||
_soundManager.reset(new SoundManager(_hWnd));
|
||||
|
||||
Console::RegisterMessageManager(_renderer.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -58,9 +60,9 @@ namespace NES {
|
|||
|
||||
int MainWindow::Run()
|
||||
{
|
||||
#if _DEBUG
|
||||
//#if _DEBUG
|
||||
CreateConsole();
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
Initialize();
|
||||
|
||||
|
@ -83,14 +85,21 @@ namespace NES {
|
|||
}
|
||||
} else {
|
||||
_renderer->Render();
|
||||
_gameServer.Exec();
|
||||
_gameClient.Exec();
|
||||
}
|
||||
|
||||
SetMenuEnabled(ID_NETPLAY_STARTSERVER, !_gameServer.Started() && !_gameClient.Connected());
|
||||
SetMenuEnabled(ID_NETPLAY_STOPSERVER, _gameServer.Started() && !_gameClient.Connected());
|
||||
|
||||
SetMenuEnabled(ID_NETPLAY_CONNECT, !_gameServer.Started() && !_gameClient.Connected());
|
||||
SetMenuEnabled(ID_NETPLAY_DISCONNECT, !_gameServer.Started() && _gameClient.Connected());
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
|
||||
|
||||
if(_playingMovie) {
|
||||
if(!Movie::Playing()) {
|
||||
_playingMovie = false;
|
||||
_renderer->DisplayMessage(L"Movie ended.", 3000);
|
||||
|
||||
//Pause game
|
||||
Stop(false);
|
||||
}
|
||||
|
@ -154,6 +163,35 @@ namespace NES {
|
|||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK MainWindow::ConnectWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(lParam);
|
||||
wchar_t hostName[1000];
|
||||
wstring lastHost;
|
||||
|
||||
switch(message) {
|
||||
case WM_INITDIALOG:
|
||||
|
||||
lastHost = ConfigManager::GetValue<wstring>(Config::LastNetPlayHost);
|
||||
SetDlgItemText(hDlg, IDC_HOSTNAME, lastHost.size() > 0 ? lastHost.c_str() : L"localhost");
|
||||
return (INT_PTR)TRUE;
|
||||
|
||||
case WM_COMMAND:
|
||||
if(LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
|
||||
if(LOWORD(wParam) == IDOK) {
|
||||
GetDlgItemText(hDlg, IDC_HOSTNAME, (LPWSTR)hostName, 1000);
|
||||
ConfigManager::SetValue(Config::LastNetPlayHost, wstring(hostName));
|
||||
MainWindow::GetInstance()->_gameClient.Connect(utf8util::UTF8FromUTF16(hostName).c_str(), 8888);
|
||||
}
|
||||
|
||||
EndDialog(hDlg, LOWORD(wParam));
|
||||
return (INT_PTR)TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (INT_PTR)FALSE;
|
||||
}
|
||||
|
||||
INT_PTR CALLBACK MainWindow::ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
@ -460,7 +498,7 @@ namespace NES {
|
|||
void MainWindow::SelectSaveSlot(int slot)
|
||||
{
|
||||
_currentSaveSlot = slot % 5;
|
||||
_renderer->DisplayMessage(L"Savestate slot: " + std::to_wstring(_currentSaveSlot + 1), 3000);
|
||||
_renderer->DisplayMessage(L"Savestate slot: " + std::to_wstring(_currentSaveSlot + 1));
|
||||
|
||||
SetMenuCheck(ID_SAVESTATESLOT_1, _currentSaveSlot == 0);
|
||||
SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1);
|
||||
|
@ -520,15 +558,10 @@ namespace NES {
|
|||
break;
|
||||
|
||||
case ID_FILE_QUICKLOAD:
|
||||
if(mainWindow->_console->LoadState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1))) {
|
||||
mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000);
|
||||
} else {
|
||||
mainWindow->_renderer->DisplayMessage(L"Slot is empty.", 3000);
|
||||
}
|
||||
mainWindow->_console->LoadState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
||||
break;
|
||||
case ID_FILE_QUICKSAVE:
|
||||
mainWindow->_console->SaveState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
|
||||
mainWindow->_renderer->DisplayMessage(L"State saved.", 3000);
|
||||
break;
|
||||
case ID_CHANGESLOT:
|
||||
mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1);
|
||||
|
@ -563,7 +596,6 @@ namespace NES {
|
|||
case ID_MOVIES_PLAY:
|
||||
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), false);
|
||||
if(!filename.empty()) {
|
||||
mainWindow->_renderer->DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true), 3000);
|
||||
mainWindow->_playingMovie = true;
|
||||
Movie::Play(filename);
|
||||
}
|
||||
|
@ -573,7 +605,6 @@ namespace NES {
|
|||
case ID_RECORDFROM_NOW:
|
||||
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), true, L"nmo");
|
||||
if(!filename.empty()) {
|
||||
mainWindow->_renderer->DisplayMessage(L"Recording...", 3000);
|
||||
Movie::Record(filename, wmId == ID_RECORDFROM_START);
|
||||
}
|
||||
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, true);
|
||||
|
@ -585,6 +616,20 @@ namespace NES {
|
|||
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, false);
|
||||
break;
|
||||
|
||||
case ID_NETPLAY_STARTSERVER:
|
||||
mainWindow->_gameServer.Start();
|
||||
break;
|
||||
case ID_NETPLAY_STOPSERVER:
|
||||
mainWindow->_gameServer.Stop();
|
||||
break;
|
||||
|
||||
case ID_NETPLAY_CONNECT:
|
||||
DialogBox(nullptr, MAKEINTRESOURCE(IDD_CONNECT), hWnd, ConnectWndProc);
|
||||
break;
|
||||
case ID_NETPLAY_DISCONNECT:
|
||||
mainWindow->_gameClient.Disconnect();
|
||||
break;
|
||||
|
||||
case ID_TESTS_RUNTESTS:
|
||||
mainWindow->RunTests();
|
||||
break;
|
||||
|
@ -641,6 +686,7 @@ namespace NES {
|
|||
break;
|
||||
|
||||
case WM_DESTROY:
|
||||
mainWindow->_gameClient.Disconnect();
|
||||
mainWindow->Stop(true);
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "SoundManager.h"
|
||||
#include "../Core/Console.h"
|
||||
#include "../Utilities/ConfigManager.h"
|
||||
#include "../Core/GameServer.h"
|
||||
#include "../Core/GameClient.h"
|
||||
|
||||
namespace NES {
|
||||
class MainWindow
|
||||
|
@ -22,6 +24,9 @@ namespace NES {
|
|||
wstring _currentROM;
|
||||
wstring _currentROMName;
|
||||
|
||||
GameClient _gameClient;
|
||||
GameServer _gameServer;
|
||||
|
||||
int _currentSaveSlot = 0;
|
||||
|
||||
bool _playingMovie = false;
|
||||
|
@ -31,6 +36,7 @@ namespace NES {
|
|||
HRESULT InitWindow();
|
||||
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ConnectWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
|
|
|
@ -276,10 +276,10 @@ namespace NES
|
|||
return shaderResourceView;
|
||||
}
|
||||
|
||||
void Renderer::DisplayMessage(wstring text, uint32_t duration)
|
||||
void Renderer::DisplayMessage(wstring text)
|
||||
{
|
||||
_displayMessage = text;
|
||||
_displayTimestamp = timeGetTime() + duration;
|
||||
_displayTimestamp = timeGetTime() + 3000; //3 secs
|
||||
}
|
||||
|
||||
void Renderer::DrawNESScreen()
|
||||
|
@ -335,7 +335,7 @@ namespace NES
|
|||
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 142, (float)_hdScreenHeight / 2 - 77), Colors::Black, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
||||
_font->DrawString(_spriteBatch.get(), L"PAUSED", XMFLOAT2((float)_hdScreenWidth / 2 - 145, (float)_hdScreenHeight / 2 - 80), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 2.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
HRESULT Renderer::CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
|
||||
{
|
||||
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
|
@ -353,7 +353,7 @@ namespace NES
|
|||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
}*/
|
||||
|
||||
void Renderer::Render()
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "DirectXTK\SpriteBatch.h"
|
||||
#include "DirectXTK\SpriteFont.h"
|
||||
#include "../Core/IVideoDevice.h"
|
||||
#include "../Core/IMessageManager.h"
|
||||
|
||||
using namespace DirectX;
|
||||
|
||||
|
@ -12,7 +13,7 @@ namespace NES {
|
|||
ShowPauseScreen = 2,
|
||||
};
|
||||
|
||||
class Renderer : IVideoDevice
|
||||
class Renderer : public IVideoDevice, public IMessageManager
|
||||
{
|
||||
private:
|
||||
HWND _hWnd = nullptr;
|
||||
|
@ -39,7 +40,7 @@ namespace NES {
|
|||
byte* _overlayBuffer = nullptr;
|
||||
|
||||
std::unique_ptr<SpriteBatch> _spriteBatch;
|
||||
ID3D11PixelShader* _pixelShader = nullptr;
|
||||
//ID3D11PixelShader* _pixelShader = nullptr;
|
||||
|
||||
uint32_t _screenWidth;
|
||||
uint32_t _screenHeight;
|
||||
|
@ -63,7 +64,7 @@ namespace NES {
|
|||
void DrawNESScreen();
|
||||
void DrawPauseScreen();
|
||||
|
||||
HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
|
||||
//HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
|
||||
|
||||
public:
|
||||
Renderer(HWND hWnd);
|
||||
|
@ -71,7 +72,7 @@ namespace NES {
|
|||
|
||||
void Render();
|
||||
|
||||
void Renderer::DisplayMessage(wstring text, uint32_t duration);
|
||||
void DisplayMessage(wstring text);
|
||||
|
||||
void SetFlags(uint32_t flags)
|
||||
{
|
||||
|
|
BIN
GUI/resource.h
BIN
GUI/resource.h
Binary file not shown.
|
@ -47,6 +47,11 @@
|
|||
#include <Fcntl.h>
|
||||
#include <thread>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
using std::thread;
|
||||
using std::list;
|
||||
using std::vector;
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
|
|
@ -11,6 +11,7 @@ string ConfigManager::ConfigTagNames[] = {
|
|||
"MRU3",
|
||||
"MRU4",
|
||||
"LastGameFolder",
|
||||
"LastNetPlayHost",
|
||||
"Player1_ButtonA",
|
||||
"Player1_ButtonB",
|
||||
"Player1_Select",
|
||||
|
|
|
@ -14,6 +14,7 @@ enum class Config
|
|||
MRU3,
|
||||
MRU4,
|
||||
LastGameFolder,
|
||||
LastNetPlayHost,
|
||||
Player1_ButtonA,
|
||||
Player1_ButtonB,
|
||||
Player1_Select,
|
||||
|
|
35
Utilities/SimpleLock.h
Normal file
35
Utilities/SimpleLock.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
|
||||
class SimpleLock
|
||||
{
|
||||
private:
|
||||
atomic_flag _lock;
|
||||
|
||||
public:
|
||||
void Acquire()
|
||||
{
|
||||
while(_lock.test_and_set());
|
||||
}
|
||||
|
||||
bool IsFree()
|
||||
{
|
||||
if(!_lock.test_and_set()) {
|
||||
_lock.clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WaitForRelease()
|
||||
{
|
||||
//Wait until we are able to grab a lock, and then release it again
|
||||
Acquire();
|
||||
Release();
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
_lock.clear();
|
||||
}
|
||||
};
|
206
Utilities/Socket.h
Normal file
206
Utilities/Socket.h
Normal file
|
@ -0,0 +1,206 @@
|
|||
#pragma once
|
||||
#pragma comment(lib,"ws2_32.lib") //Winsock Library
|
||||
#include "stdafx.h"
|
||||
#include <winsock2.h>
|
||||
|
||||
class Socket
|
||||
{
|
||||
private:
|
||||
SOCKET _socket = INVALID_SOCKET;
|
||||
bool _connectionError = false;
|
||||
bool _cleanupWSA = false;
|
||||
char* _sendBuffer;
|
||||
int _bufferPosition;
|
||||
|
||||
public:
|
||||
Socket()
|
||||
{
|
||||
WSADATA wsaDat;
|
||||
if(WSAStartup(MAKEWORD(2, 2), &wsaDat) != 0) {
|
||||
std::cout << "WSAStartup failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
_cleanupWSA = true;
|
||||
|
||||
_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(_socket == INVALID_SOCKET) {
|
||||
std::cout << "Socket creation failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
}
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
Socket(SOCKET socket)
|
||||
{
|
||||
_socket = socket;
|
||||
|
||||
if(socket == INVALID_SOCKET) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
SetSocketOptions();
|
||||
}
|
||||
|
||||
_sendBuffer = new char[200000];
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
~Socket()
|
||||
{
|
||||
if(_socket != INVALID_SOCKET) {
|
||||
Close();
|
||||
}
|
||||
if(_cleanupWSA) {
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
delete[] _sendBuffer;
|
||||
}
|
||||
|
||||
void SetSocketOptions()
|
||||
{
|
||||
//Non-blocking mode
|
||||
u_long iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
//Set send/recv buffers to 256k
|
||||
int bufferSize = 0x40000;
|
||||
setsockopt(_socket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize, sizeof(int));
|
||||
setsockopt(_socket, SOL_SOCKET, SO_SNDBUF, (char*)&bufferSize, sizeof(int));
|
||||
|
||||
//Disable nagle's algorithm to improve latency
|
||||
u_long value = 1;
|
||||
setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
|
||||
}
|
||||
|
||||
void SetConnectionErrorFlag()
|
||||
{
|
||||
_connectionError = true;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
std::cout << "Client disconnected!\r\n";
|
||||
shutdown(_socket, SD_SEND);
|
||||
closesocket(_socket);
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
bool ConnectionError()
|
||||
{
|
||||
return _connectionError;
|
||||
}
|
||||
|
||||
void Bind(u_short port)
|
||||
{
|
||||
SOCKADDR_IN serverInf;
|
||||
serverInf.sin_family = AF_INET;
|
||||
serverInf.sin_addr.s_addr = INADDR_ANY;
|
||||
serverInf.sin_port = htons(port);
|
||||
|
||||
if(bind(_socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR) {
|
||||
std::cout << "Unable to bind socket." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
bool Connect(const char* hostname, u_short port)
|
||||
{
|
||||
// Resolve IP address for hostname
|
||||
struct hostent *host;
|
||||
if((host = gethostbyname(hostname)) == NULL) {
|
||||
std::cout << "Failed to resolve hostname." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
// Setup our socket address structure
|
||||
SOCKADDR_IN SockAddr;
|
||||
SockAddr.sin_port = htons(port);
|
||||
SockAddr.sin_family = AF_INET;
|
||||
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
|
||||
|
||||
u_long iMode = 0;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
// Attempt to connect to server
|
||||
if(connect(_socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR) {
|
||||
std::cout << "Failed to establish connection with server." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
iMode = 1;
|
||||
ioctlsocket(_socket, FIONBIO, &iMode);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Listen(int backlog)
|
||||
{
|
||||
if(listen(_socket, backlog) == SOCKET_ERROR) {
|
||||
std::cout << "listen failed." << std::endl;
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<Socket> Accept(sockaddr* addr, int *addrlen)
|
||||
{
|
||||
SOCKET socket = accept(_socket, addr, addrlen);
|
||||
return shared_ptr<Socket>(new Socket(socket));
|
||||
}
|
||||
|
||||
int Send(char *buf, int len, int flags)
|
||||
{
|
||||
int nError;
|
||||
int returnVal;
|
||||
do {
|
||||
//Loop until everything has been sent (shouldn't loop at all in the vast majority of cases)
|
||||
returnVal = send(_socket, buf, len, flags);
|
||||
|
||||
nError = WSAGetLastError();
|
||||
if(nError != 0) {
|
||||
if(nError != WSAEWOULDBLOCK) {
|
||||
SetConnectionErrorFlag();
|
||||
} else {
|
||||
if(returnVal > 0) {
|
||||
//Sent partial data, adjust pointer & length
|
||||
buf += returnVal;
|
||||
len -= returnVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(nError == WSAEWOULDBLOCK);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void BufferedSend(char *buf, int len)
|
||||
{
|
||||
memcpy(_sendBuffer+_bufferPosition, buf, len);
|
||||
_bufferPosition += len;
|
||||
}
|
||||
|
||||
void SendBuffer()
|
||||
{
|
||||
Send(_sendBuffer, _bufferPosition, 0);
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
|
||||
int Recv(char *buf, int len, int flags)
|
||||
{
|
||||
static ofstream received("received.log", ios::out | ios::binary);
|
||||
int returnVal = recv(_socket, buf, len, flags);
|
||||
|
||||
int nError = WSAGetLastError();
|
||||
if(nError != WSAEWOULDBLOCK && nError != 0) {
|
||||
SetConnectionErrorFlag();
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
};
|
|
@ -73,6 +73,9 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="ConfigManager.h" />
|
||||
<ClInclude Include="FolderUtilities.h" />
|
||||
<ClInclude Include="NATPortMapper.h" />
|
||||
<ClInclude Include="SimpleLock.h" />
|
||||
<ClInclude Include="Socket.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
|
|
|
@ -42,6 +42,15 @@
|
|||
<ClInclude Include="FolderUtilities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Socket.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SimpleLock.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NATPortMapper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
using std::atomic_flag;
|
||||
using std::shared_ptr;
|
||||
using std::ifstream;
|
||||
using std::string;
|
||||
|
|
Loading…
Add table
Reference in a new issue