Using Command pattern andEntityX
This commit is contained in:
12
protocol.md
Normal file
12
protocol.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
## Loading Screen
|
||||||
|
|
||||||
|
- [Client]
|
||||||
|
Client sends unique UserID.
|
||||||
|
|
||||||
|
- [Server]
|
||||||
|
assign slot to Client
|
||||||
|
|
||||||
|
- [Client]
|
||||||
|
Send User Name
|
||||||
|
|
||||||
|
- [Server_START_UPDATE_ROUTINE]
|
||||||
@@ -1,31 +1,48 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// Vendor
|
||||||
#include <yojimbo.h>
|
#include <yojimbo.h>
|
||||||
|
|
||||||
|
// Own
|
||||||
#include "GameServer.h"
|
#include "GameServer.h"
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace yojimbo;
|
using namespace yojimbo;
|
||||||
|
|
||||||
static GameServer *serv;
|
static GameServer *serv;
|
||||||
|
|
||||||
void interrupt_handler( int /*dummy*/ )
|
void interrupt_handler(int /*dummy*/)
|
||||||
{
|
{
|
||||||
serv->m_running = false;
|
serv->m_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if ( !InitializeYojimbo() )
|
//--- Setup CMD
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE hInput;
|
||||||
|
DWORD prev_mode;
|
||||||
|
hInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
GetConsoleMode(hInput, &prev_mode);
|
||||||
|
SetConsoleMode(hInput, prev_mode & ENABLE_EXTENDED_FLAGS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!InitializeYojimbo())
|
||||||
{
|
{
|
||||||
printf( "error: failed to initialize Yojimbo!\n" );
|
printf("error: failed to initialize Yojimbo!\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
yojimbo_log_level( YOJIMBO_LOG_LEVEL_INFO );
|
yojimbo_log_level(YOJIMBO_LOG_LEVEL_INFO);
|
||||||
|
|
||||||
std::cout << "Yojimbo lives!" << std::endl;
|
std::cout << "Yojimbo lives!" << std::endl;
|
||||||
signal( SIGINT, interrupt_handler );
|
signal(SIGINT, interrupt_handler);
|
||||||
|
|
||||||
yojimbo::Address addr(127,0,0,1,9999);
|
yojimbo::Address addr(127, 0, 0, 1, 9999);
|
||||||
GameServer server(addr);
|
GameServer server(addr);
|
||||||
serv = &server; //For interrupt
|
serv = &server; //For interrupt
|
||||||
server.Run();
|
server.Run();
|
||||||
|
|||||||
@@ -1,36 +1,23 @@
|
|||||||
#include "Gameserver.h"
|
#include "Gameserver.h"
|
||||||
|
|
||||||
|
//std
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
//own
|
||||||
|
#include "messages/Command.h"
|
||||||
|
#include "core/Components.h"
|
||||||
|
|
||||||
using namespace yojimbo;
|
using namespace yojimbo;
|
||||||
|
|
||||||
yojimbo::MessageFactory *GameAdapter::CreateMessageFactory(yojimbo::Allocator &allocator)
|
GameServer::GameServer(const yojimbo::Address &address) : m_time(yojimbo_time()),
|
||||||
{
|
m_adapter(this),
|
||||||
return YOJIMBO_NEW(allocator, GameMessageFactory, allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameAdapter::OnServerClientConnected(int clientIndex)
|
|
||||||
{
|
|
||||||
if (m_server != NULL)
|
|
||||||
{
|
|
||||||
m_server->ClientConnected(clientIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameAdapter::OnServerClientDisconnected(int clientIndex)
|
|
||||||
{
|
|
||||||
if (m_server != NULL)
|
|
||||||
{
|
|
||||||
m_server->ClientDisconnected(clientIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameServer::GameServer(const yojimbo::Address &address) : m_adapter(this),
|
|
||||||
m_server(yojimbo::GetDefaultAllocator(),
|
m_server(yojimbo::GetDefaultAllocator(),
|
||||||
DEFAULT_PRIVATE_KEY,
|
DEFAULT_PRIVATE_KEY,
|
||||||
address,
|
address,
|
||||||
m_connectionConfig,
|
m_connectionConfig,
|
||||||
m_adapter, 0.0),
|
m_adapter,
|
||||||
|
m_time),
|
||||||
m_running(false)
|
m_running(false)
|
||||||
{
|
{
|
||||||
// start the server
|
// start the server
|
||||||
@@ -49,11 +36,17 @@ GameServer::GameServer(const yojimbo::Address &address) : m_adapter(this),
|
|||||||
|
|
||||||
void GameServer::ClientConnected(int clientIndex)
|
void GameServer::ClientConnected(int clientIndex)
|
||||||
{
|
{
|
||||||
|
entityx::Entity entity = ex.entities.create();
|
||||||
|
entity.assign<NetworkPlayerProperties>();
|
||||||
|
serverToEntityX.insert(std::pair<int, entityx::Entity>(clientIndex, entity));
|
||||||
std::cout << "client " << clientIndex << " connected" << std::endl;
|
std::cout << "client " << clientIndex << " connected" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::ClientDisconnected(int clientIndex)
|
void GameServer::ClientDisconnected(int clientIndex)
|
||||||
{
|
{
|
||||||
|
entityx::Entity entity = serverToEntityX.at(clientIndex);
|
||||||
|
entity.destroy();
|
||||||
|
serverToEntityX.erase(clientIndex);
|
||||||
std::cout << "client " << clientIndex << " disconnected" << std::endl;
|
std::cout << "client " << clientIndex << " disconnected" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +103,11 @@ void GameServer::ProcessMessages()
|
|||||||
yojimbo::Message *message = m_server.ReceiveMessage(i, j);
|
yojimbo::Message *message = m_server.ReceiveMessage(i, j);
|
||||||
while (message != NULL)
|
while (message != NULL)
|
||||||
{
|
{
|
||||||
ProcessMessage(i, message);
|
std::unique_ptr<Command> command(Command::create(i, message));
|
||||||
|
if (command)
|
||||||
|
{
|
||||||
|
command->execute(this);
|
||||||
|
}
|
||||||
m_server.ReleaseMessage(i, message);
|
m_server.ReleaseMessage(i, message);
|
||||||
message = m_server.ReceiveMessage(i, j);
|
message = m_server.ReceiveMessage(i, j);
|
||||||
}
|
}
|
||||||
@@ -119,19 +116,20 @@ void GameServer::ProcessMessages()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::ProcessMessage(int clientIndex, yojimbo::Message *message)
|
ServerAdapter::ServerAdapter(GameServer *server) : m_server(server) {}
|
||||||
|
|
||||||
|
void ServerAdapter::OnServerClientConnected(int clientIndex)
|
||||||
{
|
{
|
||||||
switch (message->GetType())
|
if (m_server != NULL)
|
||||||
{
|
{
|
||||||
case (int)GameMessageType::TEST:
|
m_server->ClientConnected(clientIndex);
|
||||||
ProcessTestMessage(clientIndex, (TestMessage *)message);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameServer::ProcessTestMessage(int clientIndex, TestMessage *message)
|
void ServerAdapter::OnServerClientDisconnected(int clientIndex)
|
||||||
{
|
{
|
||||||
// ... process test message ...
|
if (m_server != NULL)
|
||||||
|
{
|
||||||
|
m_server->ClientDisconnected(clientIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
124
src/GameServer.h
124
src/GameServer.h
@@ -1,98 +1,60 @@
|
|||||||
#pragma once
|
#ifndef GAMESERVER_H
|
||||||
|
#define GAMESERVER_H
|
||||||
|
|
||||||
#include <yojimbo.h>
|
//Std
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
//Vendor
|
||||||
|
#include <yojimbo.h>
|
||||||
|
#include <entityx/entityx.h>
|
||||||
|
|
||||||
|
//Own
|
||||||
|
#include "Shared.h"
|
||||||
|
|
||||||
|
static const int MAX_PLAYERS = 64;
|
||||||
|
|
||||||
class GameServer;
|
class GameServer;
|
||||||
|
|
||||||
// a simple test message
|
class ServerAdapter : public SharedAdapter
|
||||||
enum class GameMessageType
|
|
||||||
{
|
{
|
||||||
TEST,
|
public:
|
||||||
COUNT
|
explicit ServerAdapter(GameServer *server);
|
||||||
|
|
||||||
|
void OnServerClientConnected(int clientIndex) override;
|
||||||
|
|
||||||
|
void OnServerClientDisconnected(int clientIndex) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameServer *m_server;
|
||||||
};
|
};
|
||||||
|
|
||||||
// two channels, one for each type that Yojimbo supports
|
|
||||||
enum class GameChannel
|
|
||||||
{
|
|
||||||
RELIABLE,
|
|
||||||
UNRELIABLE,
|
|
||||||
COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
// the client and server config
|
|
||||||
struct GameConnectionConfig : yojimbo::ClientServerConfig
|
|
||||||
{
|
|
||||||
GameConnectionConfig()
|
|
||||||
{
|
|
||||||
numChannels = 2;
|
|
||||||
channel[(int)GameChannel::RELIABLE].type = yojimbo::CHANNEL_TYPE_RELIABLE_ORDERED;
|
|
||||||
channel[(int)GameChannel::UNRELIABLE].type = yojimbo::CHANNEL_TYPE_UNRELIABLE_UNORDERED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestMessage : public yojimbo::Message
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int m_data;
|
|
||||||
|
|
||||||
TestMessage() : m_data(0) {}
|
|
||||||
|
|
||||||
template <typename Stream>
|
|
||||||
bool Serialize(Stream &stream)
|
|
||||||
{
|
|
||||||
serialize_int(stream, m_data, 0, 512);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
YOJIMBO_VIRTUAL_SERIALIZE_FUNCTIONS();
|
|
||||||
};
|
|
||||||
|
|
||||||
// the adapter
|
|
||||||
class GameAdapter : public yojimbo::Adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit GameAdapter(GameServer *server = NULL) : m_server(server) {}
|
|
||||||
|
|
||||||
void OnServerClientConnected(int clientIndex) override;
|
|
||||||
|
|
||||||
void OnServerClientDisconnected(int clientIndex) override;
|
|
||||||
|
|
||||||
yojimbo::MessageFactory* CreateMessageFactory(yojimbo::Allocator& allocator) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
GameServer *m_server;
|
|
||||||
};
|
|
||||||
|
|
||||||
// the message factory
|
|
||||||
YOJIMBO_MESSAGE_FACTORY_START(GameMessageFactory, (int)GameMessageType::COUNT);
|
|
||||||
YOJIMBO_DECLARE_MESSAGE_TYPE((int)GameMessageType::TEST, TestMessage);
|
|
||||||
YOJIMBO_MESSAGE_FACTORY_FINISH();
|
|
||||||
|
|
||||||
static const uint8_t DEFAULT_PRIVATE_KEY[yojimbo::KeyBytes] = {0};
|
|
||||||
static const int MAX_PLAYERS = 64;
|
|
||||||
|
|
||||||
class GameServer
|
class GameServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GameServer(const yojimbo::Address &address);
|
GameServer(const yojimbo::Address &address);
|
||||||
|
|
||||||
void ClientConnected(int clientIndex);
|
void ClientConnected(int clientIndex);
|
||||||
void ClientDisconnected(int clientIndex);
|
void ClientDisconnected(int clientIndex);
|
||||||
|
|
||||||
void ProcessMessages();
|
void ProcessCommand(std::string command);
|
||||||
void ProcessMessage(int clientIndex, yojimbo::Message *message);
|
void ProcessMessages();
|
||||||
void ProcessTestMessage(int clientIndex, TestMessage *message);
|
void ProcessMessage(int clientIndex, yojimbo::Message *message);
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
void Update(float dt);
|
void Update(float dt);
|
||||||
|
|
||||||
bool m_running;
|
bool m_running;
|
||||||
|
|
||||||
private:
|
entityx::EntityX ex;
|
||||||
GameConnectionConfig m_connectionConfig;
|
std::map<int, entityx::Entity> serverToEntityX;
|
||||||
yojimbo::Server m_server;
|
|
||||||
GameAdapter m_adapter;
|
|
||||||
|
|
||||||
double m_time;
|
private:
|
||||||
|
double m_time;
|
||||||
|
|
||||||
|
GameConnectionConfig m_connectionConfig;
|
||||||
|
ServerAdapter m_adapter;
|
||||||
|
yojimbo::Server m_server;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif //GAMESERVER_H
|
||||||
87
src/Shared.h
Normal file
87
src/Shared.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#ifndef SHARED_H
|
||||||
|
#define SHARED_H
|
||||||
|
|
||||||
|
#include <yojimbo.h>
|
||||||
|
|
||||||
|
// a simple test message
|
||||||
|
enum class GameMessageType
|
||||||
|
{
|
||||||
|
TEST,
|
||||||
|
SETNAME,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
// two channels, one for each type that Yojimbo supports
|
||||||
|
enum class GameChannel
|
||||||
|
{
|
||||||
|
RELIABLE,
|
||||||
|
UNRELIABLE,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
// the client and server config
|
||||||
|
struct GameConnectionConfig : yojimbo::ClientServerConfig
|
||||||
|
{
|
||||||
|
GameConnectionConfig()
|
||||||
|
{
|
||||||
|
numChannels = 2;
|
||||||
|
channel[(int)GameChannel::RELIABLE].type = yojimbo::CHANNEL_TYPE_RELIABLE_ORDERED;
|
||||||
|
channel[(int)GameChannel::UNRELIABLE].type = yojimbo::CHANNEL_TYPE_UNRELIABLE_UNORDERED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestMessage : public yojimbo::Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int m_data;
|
||||||
|
|
||||||
|
TestMessage() : m_data(0) {}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
bool Serialize(Stream &stream)
|
||||||
|
{
|
||||||
|
serialize_int(stream, m_data, 0, 512);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
YOJIMBO_VIRTUAL_SERIALIZE_FUNCTIONS();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetNameMessage : public yojimbo::Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char buffer[yojimbo::MaxAddressLength];
|
||||||
|
SetNameMessage() {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
bool Serialize(Stream &stream)
|
||||||
|
{
|
||||||
|
serialize_string(stream, buffer, sizeof(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
YOJIMBO_VIRTUAL_SERIALIZE_FUNCTIONS();
|
||||||
|
};
|
||||||
|
|
||||||
|
// the message factory
|
||||||
|
YOJIMBO_MESSAGE_FACTORY_START(GameMessageFactory, (int)GameMessageType::COUNT);
|
||||||
|
YOJIMBO_DECLARE_MESSAGE_TYPE((int)GameMessageType::TEST, TestMessage);
|
||||||
|
YOJIMBO_DECLARE_MESSAGE_TYPE((int)GameMessageType::SETNAME, SetNameMessage);
|
||||||
|
YOJIMBO_MESSAGE_FACTORY_FINISH();
|
||||||
|
|
||||||
|
// the adapter
|
||||||
|
class SharedAdapter : public yojimbo::Adapter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
yojimbo::MessageFactory *CreateMessageFactory(yojimbo::Allocator &allocator) override
|
||||||
|
{
|
||||||
|
return YOJIMBO_NEW(allocator, GameMessageFactory, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t DEFAULT_PRIVATE_KEY[yojimbo::KeyBytes] = {0};
|
||||||
|
|
||||||
|
#endif //SHARED_H
|
||||||
11
src/core/Components.h
Normal file
11
src/core/Components.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct NetworkPlayerProperties
|
||||||
|
{
|
||||||
|
NetworkPlayerProperties(std::string name = "unknown")
|
||||||
|
: name(name) {}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
21
src/messages/Command.cpp
Normal file
21
src/messages/Command.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "Command.h"
|
||||||
|
#include "../Shared.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
Command *Command::create(int clientIndex, yojimbo::Message *message)
|
||||||
|
{
|
||||||
|
switch (message->GetType())
|
||||||
|
{
|
||||||
|
case (int)GameMessageType::TEST:
|
||||||
|
return new TestCommand(clientIndex, message);
|
||||||
|
break;
|
||||||
|
case (int)GameMessageType::SETNAME:
|
||||||
|
return new SetNameCommand(clientIndex, message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cout << "Unknown message type received!" << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/messages/Command.h
Normal file
44
src/messages/Command.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
//Vendor
|
||||||
|
#include <yojimbo.h>
|
||||||
|
#include "../GameServer.h"
|
||||||
|
|
||||||
|
// ---- Interface:
|
||||||
|
|
||||||
|
class Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Create Commands from Network messages (Limited list)
|
||||||
|
static Command *create(int clientIndex, yojimbo::Message *message);
|
||||||
|
// Create Commands from Strings (Limited list)
|
||||||
|
virtual void execute(GameServer *server) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- implemented Classes:
|
||||||
|
|
||||||
|
class TestCommand : public Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestCommand(int clientIndex, yojimbo::Message *message)
|
||||||
|
: m_clientIndex(clientIndex), m_message((TestMessage *)message) {}
|
||||||
|
|
||||||
|
virtual void execute(GameServer *server) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_clientIndex;
|
||||||
|
TestMessage *m_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetNameCommand : public Command
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SetNameCommand(int clientIndex, yojimbo::Message *message)
|
||||||
|
: m_clientIndex(clientIndex), m_message((SetNameMessage *)message) {}
|
||||||
|
|
||||||
|
virtual void execute(GameServer *server) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_clientIndex;
|
||||||
|
SetNameMessage *m_message;
|
||||||
|
};
|
||||||
14
src/messages/SetNameCommand.cpp
Normal file
14
src/messages/SetNameCommand.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
#include "../core/Components.h"
|
||||||
|
|
||||||
|
void SetNameCommand::execute(GameServer *server)
|
||||||
|
{
|
||||||
|
entityx::Entity entity = server->serverToEntityX.at(m_clientIndex);
|
||||||
|
entityx::ComponentHandle<NetworkPlayerProperties> playerproperties = entity.component<NetworkPlayerProperties>();
|
||||||
|
if (playerproperties)
|
||||||
|
{
|
||||||
|
playerproperties->name = std::string(m_message->buffer);
|
||||||
|
}
|
||||||
|
std::cout << "Entity name changed to " << playerproperties->name << " Buffer content: " << std::string(m_message->buffer) << std::endl;
|
||||||
|
}
|
||||||
12
src/messages/TestCommand.cpp
Normal file
12
src/messages/TestCommand.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
#include "../core/Components.h"
|
||||||
|
|
||||||
|
void TestCommand::execute(GameServer *server)
|
||||||
|
{
|
||||||
|
// ... process test message ...
|
||||||
|
entityx::Entity entity = server->serverToEntityX.at(m_clientIndex);
|
||||||
|
entityx::ComponentHandle<NetworkPlayerProperties> playerproperties = entity.component<NetworkPlayerProperties>();
|
||||||
|
|
||||||
|
std::cout << "Test message received from client " << playerproperties->name << ":" << m_clientIndex << " with data: " << m_message->m_data << std::endl;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user