Netplay (not finished, but works), save state fixes

This commit is contained in:
Souryo 2014-07-06 19:54:47 -04:00
parent 021efccbed
commit f4802421d0
30 changed files with 979 additions and 79 deletions

View file

@ -78,9 +78,8 @@ bool APU::Exec(uint32_t executedCycles)
if(availableSampleCount >= APU::SamplesPerFrame) { if(availableSampleCount >= APU::SamplesPerFrame) {
size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame); size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame);
APU::AudioDevice->PlayBuffer(_outputBuffer, sampleCount * BitsPerSample / 8); APU::AudioDevice->PlayBuffer(_outputBuffer, sampleCount * BitsPerSample / 8);
return true;
} }
return true;
} }
return false; return false;
} }
@ -92,6 +91,8 @@ void APU::StreamState(bool saving)
_apu.save_snapshot(&snapshot); _apu.save_snapshot(&snapshot);
} }
Stream<uint32_t>(_currentClock);
StreamArray<uint8_t>(snapshot.w40xx, 0x14); StreamArray<uint8_t>(snapshot.w40xx, 0x14);
Stream<uint8_t>(snapshot.w4015); Stream<uint8_t>(snapshot.w4015);
Stream<uint8_t>(snapshot.w4017); Stream<uint8_t>(snapshot.w4017);

View file

@ -4,11 +4,11 @@
#include "../Utilities/Timer.h" #include "../Utilities/Timer.h"
Console* Console::Instance = nullptr; Console* Console::Instance = nullptr;
IMessageManager* Console::MessageManager = nullptr;
uint32_t Console::Flags = 0; uint32_t Console::Flags = 0;
uint32_t Console::CurrentFPS = 0; uint32_t Console::CurrentFPS = 0;
atomic_flag Console::PauseFlag; SimpleLock Console::PauseLock;
atomic_flag Console::RunningFlag; SimpleLock Console::RunningLock;
Console::Console(wstring filename) Console::Console(wstring filename)
{ {
@ -29,16 +29,16 @@ Console::Console(wstring filename)
ResetComponents(false); ResetComponents(false);
Console::PauseFlag.clear(); Console::PauseLock.Release();
Console::RunningFlag.clear(); Console::RunningLock.Release();
Console::Instance = this; Console::Instance = this;
} }
Console::~Console() Console::~Console()
{ {
Movie::Stop(); Movie::Stop();
Console::PauseFlag.clear(); Console::PauseLock.Release();
Console::RunningFlag.clear(); Console::RunningLock.Release();
if(Console::Instance == this) { if(Console::Instance == this) {
Console::Instance = nullptr; Console::Instance = nullptr;
} }
@ -66,16 +66,16 @@ void Console::Stop()
void Console::Pause() void Console::Pause()
{ {
while(Console::PauseFlag.test_and_set()); Console::PauseLock.Acquire();
//Spin wait until emu pauses //Spin wait until emu pauses
while(Console::RunningFlag.test_and_set()); Console::RunningLock.Acquire();
} }
void Console::Resume() void Console::Resume()
{ {
Console::RunningFlag.clear(); Console::RunningLock.Release();
Console::PauseFlag.clear(); Console::PauseLock.Release();
} }
void Console::SetFlags(int flags) void Console::SetFlags(int flags)
@ -93,6 +93,18 @@ bool Console::CheckFlag(int flag)
return (Console::Flags & flag) == 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() uint32_t Console::GetFPS()
{ {
return Console::CurrentFPS; return Console::CurrentFPS;
@ -105,35 +117,18 @@ void Console::Run()
uint32_t lastFrameCount = 0; uint32_t lastFrameCount = 0;
double elapsedTime = 0; double elapsedTime = 0;
double targetTime = 16.6666666666666666; double targetTime = 16.6666666666666666;
while(Console::RunningFlag.test_and_set());
Console::RunningLock.Acquire();
while(true) { while(true) {
uint32_t executedCycles = _cpu->Exec(); uint32_t executedCycles = _cpu->Exec();
_ppu->Exec(); _ppu->Exec();
bool frameDone = _apu->Exec(executedCycles); bool frameDone = _apu->Exec(executedCycles);
if(frameDone) { if(frameDone) {
_cpu->EndFrame(); _cpu->EndFrame();
_ppu->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) { if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) {
elapsedTime = clockTimer.GetElapsedMS(); elapsedTime = clockTimer.GetElapsedMS();
while(targetTime > elapsedTime) { while(targetTime > elapsedTime) {
@ -142,18 +137,34 @@ void Console::Run()
} }
elapsedTime = clockTimer.GetElapsedMS(); elapsedTime = clockTimer.GetElapsedMS();
} }
clockTimer.Reset();
} }
if(fpsTimer.GetElapsedMS() > 1000) { if(!Console::PauseLock.IsFree()) {
uint32_t frameCount = _ppu->GetFrameCount(); //Need to temporarely pause the emu (to save/load a state, etc.)
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000))); Console::RunningLock.Release();
lastFrameCount = frameCount;
fpsTimer.Reset(); //Spin wait until we are allowed to start again
Console::PauseLock.WaitForRelease();
Console::RunningLock.Acquire();
}
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) void Console::SaveState(wstring filename)
@ -165,6 +176,8 @@ void Console::SaveState(wstring filename)
Console::SaveState(file); Console::SaveState(file);
Console::Resume(); Console::Resume();
file.close(); file.close();
Console::DisplayMessage(L"State saved.");
} }
} }
@ -177,27 +190,45 @@ bool Console::LoadState(wstring filename)
Console::LoadState(file); Console::LoadState(file);
Console::Resume(); Console::Resume();
file.close(); file.close();
Console::DisplayMessage(L"State loaded.");
return true; return true;
} }
Console::DisplayMessage(L"Slot is empty.");
return false; return false;
} }
void Console::SaveState(ostream &saveStream) void Console::SaveState(ostream &saveStream)
{ {
Instance->_cpu->SaveSnapshot(&saveStream); if(Instance) {
Instance->_ppu->SaveSnapshot(&saveStream); Instance->_cpu->SaveSnapshot(&saveStream);
Instance->_memoryManager->SaveSnapshot(&saveStream); Instance->_ppu->SaveSnapshot(&saveStream);
Instance->_mapper->SaveSnapshot(&saveStream); Instance->_memoryManager->SaveSnapshot(&saveStream);
Instance->_apu->SaveSnapshot(&saveStream); Instance->_mapper->SaveSnapshot(&saveStream);
Instance->_apu->SaveSnapshot(&saveStream);
Instance->_controlManager->SaveSnapshot(&saveStream);
}
} }
void Console::LoadState(istream &loadStream) void Console::LoadState(istream &loadStream)
{ {
Instance->_cpu->LoadSnapshot(&loadStream); if(Instance) {
Instance->_ppu->LoadSnapshot(&loadStream); Instance->_cpu->LoadSnapshot(&loadStream);
Instance->_memoryManager->LoadSnapshot(&loadStream); Instance->_ppu->LoadSnapshot(&loadStream);
Instance->_mapper->LoadSnapshot(&loadStream); Instance->_memoryManager->LoadSnapshot(&loadStream);
Instance->_apu->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) bool Console::RunTest(uint8_t *expectedResult)

View file

@ -7,6 +7,8 @@
#include "BaseMapper.h" #include "BaseMapper.h"
#include "MemoryManager.h" #include "MemoryManager.h"
#include "ControlManager.h" #include "ControlManager.h"
#include "../Utilities/SimpleLock.h"
#include "IMessageManager.h"
enum EmulationFlags enum EmulationFlags
{ {
@ -19,8 +21,9 @@ class Console
static Console* Instance; static Console* Instance;
static uint32_t Flags; static uint32_t Flags;
static uint32_t CurrentFPS; static uint32_t CurrentFPS;
static atomic_flag PauseFlag; static SimpleLock PauseLock;
static atomic_flag RunningFlag; static SimpleLock RunningLock;
static IMessageManager* MessageManager;
unique_ptr<CPU> _cpu; unique_ptr<CPU> _cpu;
unique_ptr<PPU> _ppu; unique_ptr<PPU> _ppu;
@ -56,9 +59,13 @@ class Console
static void SaveState(ostream &saveStream); static void SaveState(ostream &saveStream);
static bool LoadState(wstring filename); static bool LoadState(wstring filename);
static void LoadState(istream &loadStream); static void LoadState(istream &loadStream);
static void LoadState(uint8_t *buffer, uint32_t bufferSize);
static bool CheckFlag(int flag); static bool CheckFlag(int flag);
static void SetFlags(int flags); static void SetFlags(int flags);
static void ClearFlags(int flags); static void ClearFlags(int flags);
static uint32_t GetFPS(); static uint32_t GetFPS();
static void RegisterMessageManager(IMessageManager* messageManager);
static void DisplayMessage(wstring message);
}; };

View file

@ -2,17 +2,60 @@
#include "ControlManager.h" #include "ControlManager.h"
IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr }; IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
IControlDevice* ControlManager::OriginalControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
IGameBroadcaster* ControlManager::GameBroadcaster = nullptr;
ControlManager::ControlManager() 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) void ControlManager::RegisterControlDevice(IControlDevice* controlDevice, uint8_t port)
{ {
ControlManager::ControlDevices[port] = controlDevice; 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() void ControlManager::RefreshAllPorts()
{ {
RefreshStateBuffer(0); RefreshStateBuffer(0);
@ -34,11 +77,7 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
state = Movie::Instance->GetState(port); state = Movie::Instance->GetState(port);
} else { } else {
if(controlDevice) { if(controlDevice) {
ButtonState buttonState = controlDevice->GetButtonState(); state = controlDevice->GetButtonState().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."
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);
} else { } else {
state = 0x00; state = 0x00;
} }
@ -46,6 +85,9 @@ void ControlManager::RefreshStateBuffer(uint8_t port)
//Used when recording movies //Used when recording movies
Movie::Instance->RecordState(port, state); Movie::Instance->RecordState(port, state);
if(ControlManager::GameBroadcaster) {
ControlManager::GameBroadcaster->BroadcastInput(state, port);
}
_stateBuffer[port] = state; _stateBuffer[port] = state;
} }
@ -97,3 +139,9 @@ void ControlManager::WriteRAM(uint16_t addr, uint8_t value)
} }
} }
void ControlManager::StreamState(bool saving)
{
StreamArray<uint8_t>(_stateBuffer, 4);
Stream<bool>(_refreshState);
}

View file

@ -4,11 +4,16 @@
#include "IControlDevice.h" #include "IControlDevice.h"
#include "IMemoryHandler.h" #include "IMemoryHandler.h"
#include "Movie.h" #include "Movie.h"
#include "IGameBroadcaster.h"
#include "Snapshotable.h"
class ControlManager : public IMemoryHandler class ControlManager : public Snapshotable, public IMemoryHandler
{ {
private: private:
static IControlDevice* ControlDevices[4]; static IControlDevice* ControlDevices[4];
static IControlDevice* OriginalControlDevices[4];
static IGameBroadcaster* GameBroadcaster;
bool _refreshState = false; bool _refreshState = false;
uint8_t _stateBuffer[4]; uint8_t _stateBuffer[4];
@ -16,10 +21,20 @@ class ControlManager : public IMemoryHandler
void RefreshStateBuffer(uint8_t port); void RefreshStateBuffer(uint8_t port);
uint8_t GetPortValue(uint8_t port); uint8_t GetPortValue(uint8_t port);
protected:
virtual void StreamState(bool saving);
public: public:
ControlManager(); 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 RegisterControlDevice(IControlDevice* controlDevice, uint8_t port);
static void UnregisterControlDevice(IControlDevice* controlDevice);
void GetMemoryRanges(MemoryRanges &ranges) void GetMemoryRanges(MemoryRanges &ranges)
{ {

View file

@ -42,6 +42,8 @@
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<IncludePath>$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
@ -93,12 +95,20 @@
<ClInclude Include="CNROM.h" /> <ClInclude Include="CNROM.h" />
<ClInclude Include="ColorDreams.h" /> <ClInclude Include="ColorDreams.h" />
<ClInclude Include="ControlManager.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="IAudioDevice.h" />
<ClInclude Include="IControlDevice.h" /> <ClInclude Include="IControlDevice.h" />
<ClInclude Include="IGameBroadcaster.h" />
<ClInclude Include="IMemoryHandler.h" /> <ClInclude Include="IMemoryHandler.h" />
<ClInclude Include="Console.h" /> <ClInclude Include="Console.h" />
<ClInclude Include="IMessageManager.h" />
<ClInclude Include="MMC2.h" /> <ClInclude Include="MMC2.h" />
<ClInclude Include="Movie.h" /> <ClInclude Include="Movie.h" />
<ClInclude Include="NetMessage.h" />
<ClInclude Include="Snapshotable.h" /> <ClInclude Include="Snapshotable.h" />
<ClInclude Include="IVideoDevice.h" /> <ClInclude Include="IVideoDevice.h" />
<ClInclude Include="MapperFactory.h" /> <ClInclude Include="MapperFactory.h" />
@ -123,6 +133,7 @@
<ClInclude Include="stdafx.h" /> <ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" /> <ClInclude Include="targetver.h" />
<ClInclude Include="UNROM.h" /> <ClInclude Include="UNROM.h" />
<ClInclude Include="VirtualController.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="APU.cpp" /> <ClCompile Include="APU.cpp" />

View file

@ -22,6 +22,9 @@
<Filter Include="Header Files\Mappers"> <Filter Include="Header Files\Mappers">
<UniqueIdentifier>{67b52e86-0ff2-4dbe-b9ed-7c92aace61d5}</UniqueIdentifier> <UniqueIdentifier>{67b52e86-0ff2-4dbe-b9ed-7c92aace61d5}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Header Files\NetPlay">
<UniqueIdentifier>{43f375c6-d7c7-4bbf-8520-ad53e4e1b4a3}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="stdafx.h"> <ClInclude Include="stdafx.h">
@ -132,6 +135,33 @@
<ClInclude Include="Movie.h"> <ClInclude Include="Movie.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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>
<ItemGroup> <ItemGroup>
<ClCompile Include="CPU.cpp"> <ClCompile Include="CPU.cpp">

62
Core/GameClient.h Normal file
View 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
View 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);
}
}
}
};

View 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;
}
};

View file

@ -14,6 +14,25 @@ struct ButtonState
bool Select = false; bool Select = false;
bool Start = 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 class IControlDevice

9
Core/IGameBroadcaster.h Normal file
View 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
View file

@ -0,0 +1,9 @@
#pragma once
#include "stdafx.h"
class IMessageManager
{
public:
virtual void DisplayMessage(wstring message) = 0;
};

View file

@ -43,6 +43,7 @@ uint8_t Movie::GetState(uint8_t port)
if(_readPosition[port] >= _data.DataSize[port]) { if(_readPosition[port] >= _data.DataSize[port]) {
//End of movie file //End of movie file
Console::DisplayMessage(L"Movie ended.");
_playing = false; _playing = false;
} }
@ -82,6 +83,8 @@ void Movie::StartRecording(wstring filename, bool reset)
_recording = true; _recording = true;
Console::Resume(); Console::Resume();
Console::DisplayMessage(L"Recording...");
} }
} }
@ -113,6 +116,7 @@ void Movie::PlayMovie(wstring filename)
} }
_playing = true; _playing = true;
Console::Resume(); Console::Resume();
Console::DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true));
} }
} }

150
Core/NetMessage.h Normal file
View 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();
}
};

View file

@ -19,6 +19,7 @@
#include <array> #include <array>
#include <sstream> #include <sstream>
#include <atomic> #include <atomic>
#include <list>
using std::vector; using std::vector;
using std::shared_ptr; using std::shared_ptr;
@ -32,4 +33,6 @@ using std::ofstream;
using std::wstring; using std::wstring;
using std::exception; using std::exception;
using std::atomic_flag; using std::atomic_flag;
using std::atomic;
using std::list;
using std::max; using std::max;

Binary file not shown.

View file

@ -20,6 +20,8 @@ namespace NES {
_renderer.reset(new Renderer(_hWnd)); _renderer.reset(new Renderer(_hWnd));
_soundManager.reset(new SoundManager(_hWnd)); _soundManager.reset(new SoundManager(_hWnd));
Console::RegisterMessageManager(_renderer.get());
return true; return true;
} }
@ -58,9 +60,9 @@ namespace NES {
int MainWindow::Run() int MainWindow::Run()
{ {
#if _DEBUG //#if _DEBUG
CreateConsole(); CreateConsole();
#endif //#endif
Initialize(); Initialize();
@ -83,14 +85,21 @@ namespace NES {
} }
} else { } else {
_renderer->Render(); _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)); std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
if(_playingMovie) { if(_playingMovie) {
if(!Movie::Playing()) { if(!Movie::Playing()) {
_playingMovie = false; _playingMovie = false;
_renderer->DisplayMessage(L"Movie ended.", 3000);
//Pause game //Pause game
Stop(false); Stop(false);
} }
@ -155,6 +164,35 @@ namespace NES {
return (INT_PTR)FALSE; 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) INT_PTR CALLBACK MainWindow::ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ {
UNREFERENCED_PARAMETER(lParam); UNREFERENCED_PARAMETER(lParam);
@ -460,7 +498,7 @@ namespace NES {
void MainWindow::SelectSaveSlot(int slot) void MainWindow::SelectSaveSlot(int slot)
{ {
_currentSaveSlot = slot % 5; _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_1, _currentSaveSlot == 0);
SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1); SetMenuCheck(ID_SAVESTATESLOT_2, _currentSaveSlot == 1);
@ -520,15 +558,10 @@ namespace NES {
break; break;
case ID_FILE_QUICKLOAD: case ID_FILE_QUICKLOAD:
if(mainWindow->_console->LoadState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1))) { 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);
}
break; break;
case ID_FILE_QUICKSAVE: case ID_FILE_QUICKSAVE:
mainWindow->_console->SaveState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1)); mainWindow->_console->SaveState(FolderUtilities::GetSaveStateFolder() + mainWindow->_currentROMName + L".ss" + std::to_wstring(mainWindow->_currentSaveSlot + 1));
mainWindow->_renderer->DisplayMessage(L"State saved.", 3000);
break; break;
case ID_CHANGESLOT: case ID_CHANGESLOT:
mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1); mainWindow->SelectSaveSlot(mainWindow->_currentSaveSlot + 1);
@ -563,7 +596,6 @@ namespace NES {
case ID_MOVIES_PLAY: case ID_MOVIES_PLAY:
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), false); filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), false);
if(!filename.empty()) { if(!filename.empty()) {
mainWindow->_renderer->DisplayMessage(L"Playing movie: " + FolderUtilities::GetFilename(filename, true), 3000);
mainWindow->_playingMovie = true; mainWindow->_playingMovie = true;
Movie::Play(filename); Movie::Play(filename);
} }
@ -573,7 +605,6 @@ namespace NES {
case ID_RECORDFROM_NOW: case ID_RECORDFROM_NOW:
filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), true, L"nmo"); filename = FolderUtilities::OpenFile(L"Movie Files (*.nmo)\0*.nmo\0All (*.*)\0*.*", FolderUtilities::GetMovieFolder(), true, L"nmo");
if(!filename.empty()) { if(!filename.empty()) {
mainWindow->_renderer->DisplayMessage(L"Recording...", 3000);
Movie::Record(filename, wmId == ID_RECORDFROM_START); Movie::Record(filename, wmId == ID_RECORDFROM_START);
} }
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, true); mainWindow->SetMenuEnabled(ID_MOVIES_STOP, true);
@ -585,6 +616,20 @@ namespace NES {
mainWindow->SetMenuEnabled(ID_MOVIES_STOP, false); mainWindow->SetMenuEnabled(ID_MOVIES_STOP, false);
break; 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: case ID_TESTS_RUNTESTS:
mainWindow->RunTests(); mainWindow->RunTests();
break; break;
@ -641,6 +686,7 @@ namespace NES {
break; break;
case WM_DESTROY: case WM_DESTROY:
mainWindow->_gameClient.Disconnect();
mainWindow->Stop(true); mainWindow->Stop(true);
PostQuitMessage(0); PostQuitMessage(0);
break; break;

View file

@ -3,6 +3,8 @@
#include "SoundManager.h" #include "SoundManager.h"
#include "../Core/Console.h" #include "../Core/Console.h"
#include "../Utilities/ConfigManager.h" #include "../Utilities/ConfigManager.h"
#include "../Core/GameServer.h"
#include "../Core/GameClient.h"
namespace NES { namespace NES {
class MainWindow class MainWindow
@ -22,6 +24,9 @@ namespace NES {
wstring _currentROM; wstring _currentROM;
wstring _currentROMName; wstring _currentROMName;
GameClient _gameClient;
GameServer _gameServer;
int _currentSaveSlot = 0; int _currentSaveSlot = 0;
bool _playingMovie = false; bool _playingMovie = false;
@ -31,6 +36,7 @@ namespace NES {
HRESULT InitWindow(); HRESULT InitWindow();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 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 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 InputConfig(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); static INT_PTR CALLBACK ControllerSetup(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

View file

@ -276,10 +276,10 @@ namespace NES
return shaderResourceView; return shaderResourceView;
} }
void Renderer::DisplayMessage(wstring text, uint32_t duration) void Renderer::DisplayMessage(wstring text)
{ {
_displayMessage = text; _displayMessage = text;
_displayTimestamp = timeGetTime() + duration; _displayTimestamp = timeGetTime() + 3000; //3 secs
} }
void Renderer::DrawNESScreen() 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 - 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); _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) HRESULT Renderer::CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
{ {
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS; DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
@ -353,7 +353,7 @@ namespace NES
} }
return hr; return hr;
} }*/
void Renderer::Render() void Renderer::Render()
{ {

View file

@ -2,6 +2,7 @@
#include "DirectXTK\SpriteBatch.h" #include "DirectXTK\SpriteBatch.h"
#include "DirectXTK\SpriteFont.h" #include "DirectXTK\SpriteFont.h"
#include "../Core/IVideoDevice.h" #include "../Core/IVideoDevice.h"
#include "../Core/IMessageManager.h"
using namespace DirectX; using namespace DirectX;
@ -12,7 +13,7 @@ namespace NES {
ShowPauseScreen = 2, ShowPauseScreen = 2,
}; };
class Renderer : IVideoDevice class Renderer : public IVideoDevice, public IMessageManager
{ {
private: private:
HWND _hWnd = nullptr; HWND _hWnd = nullptr;
@ -39,7 +40,7 @@ namespace NES {
byte* _overlayBuffer = nullptr; byte* _overlayBuffer = nullptr;
std::unique_ptr<SpriteBatch> _spriteBatch; std::unique_ptr<SpriteBatch> _spriteBatch;
ID3D11PixelShader* _pixelShader = nullptr; //ID3D11PixelShader* _pixelShader = nullptr;
uint32_t _screenWidth; uint32_t _screenWidth;
uint32_t _screenHeight; uint32_t _screenHeight;
@ -63,7 +64,7 @@ namespace NES {
void DrawNESScreen(); void DrawNESScreen();
void DrawPauseScreen(); void DrawPauseScreen();
HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut); //HRESULT CompileShader(wstring filename, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
public: public:
Renderer(HWND hWnd); Renderer(HWND hWnd);
@ -71,7 +72,7 @@ namespace NES {
void Render(); void Render();
void Renderer::DisplayMessage(wstring text, uint32_t duration); void DisplayMessage(wstring text);
void SetFlags(uint32_t flags) void SetFlags(uint32_t flags)
{ {

Binary file not shown.

View file

@ -47,6 +47,11 @@
#include <Fcntl.h> #include <Fcntl.h>
#include <thread> #include <thread>
#include <list>
#include <vector>
using std::thread; using std::thread;
using std::list;
using std::vector;
// TODO: reference additional headers your program requires here // TODO: reference additional headers your program requires here

View file

@ -11,6 +11,7 @@ string ConfigManager::ConfigTagNames[] = {
"MRU3", "MRU3",
"MRU4", "MRU4",
"LastGameFolder", "LastGameFolder",
"LastNetPlayHost",
"Player1_ButtonA", "Player1_ButtonA",
"Player1_ButtonB", "Player1_ButtonB",
"Player1_Select", "Player1_Select",

View file

@ -14,6 +14,7 @@ enum class Config
MRU3, MRU3,
MRU4, MRU4,
LastGameFolder, LastGameFolder,
LastNetPlayHost,
Player1_ButtonA, Player1_ButtonA,
Player1_ButtonB, Player1_ButtonB,
Player1_Select, Player1_Select,

35
Utilities/SimpleLock.h Normal file
View 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
View 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;
}
};

View file

@ -73,6 +73,9 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="ConfigManager.h" /> <ClInclude Include="ConfigManager.h" />
<ClInclude Include="FolderUtilities.h" /> <ClInclude Include="FolderUtilities.h" />
<ClInclude Include="NATPortMapper.h" />
<ClInclude Include="SimpleLock.h" />
<ClInclude Include="Socket.h" />
<ClInclude Include="stdafx.h" /> <ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" /> <ClInclude Include="targetver.h" />
<ClInclude Include="Timer.h" /> <ClInclude Include="Timer.h" />

View file

@ -42,6 +42,15 @@
<ClInclude Include="FolderUtilities.h"> <ClInclude Include="FolderUtilities.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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>
<ItemGroup> <ItemGroup>
<ClCompile Include="stdafx.cpp"> <ClCompile Include="stdafx.cpp">

View file

@ -10,7 +10,9 @@
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <atomic>
using std::atomic_flag;
using std::shared_ptr; using std::shared_ptr;
using std::ifstream; using std::ifstream;
using std::string; using std::string;