Using Command pattern andEntityX

This commit is contained in:
Julian Nießner
2019-06-02 12:57:53 +02:00
parent e024ef3f0c
commit e43d142f6a
10 changed files with 298 additions and 120 deletions

12
protocol.md Normal file
View 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]

View File

@@ -1,31 +1,48 @@
#include <signal.h>
#include <iostream>
// Vendor
#include <yojimbo.h>
// Own
#include "GameServer.h"
// Windows
#ifdef _WIN32
#include <windows.h>
#endif
using namespace yojimbo;
static GameServer *serv;
void interrupt_handler( int /*dummy*/ )
void interrupt_handler(int /*dummy*/)
{
serv->m_running = false;
}
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;
}
yojimbo_log_level( YOJIMBO_LOG_LEVEL_INFO );
yojimbo_log_level(YOJIMBO_LOG_LEVEL_INFO);
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);
serv = &server; //For interrupt
server.Run();

View File

@@ -1,36 +1,23 @@
#include "Gameserver.h"
//std
#include <iostream>
#include <memory>
//own
#include "messages/Command.h"
#include "core/Components.h"
using namespace yojimbo;
yojimbo::MessageFactory *GameAdapter::CreateMessageFactory(yojimbo::Allocator &allocator)
{
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),
GameServer::GameServer(const yojimbo::Address &address) : m_time(yojimbo_time()),
m_adapter(this),
m_server(yojimbo::GetDefaultAllocator(),
DEFAULT_PRIVATE_KEY,
address,
m_connectionConfig,
m_adapter, 0.0),
m_adapter,
m_time),
m_running(false)
{
// start the server
@@ -49,11 +36,17 @@ GameServer::GameServer(const yojimbo::Address &address) : m_adapter(this),
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;
}
void GameServer::ClientDisconnected(int clientIndex)
{
entityx::Entity entity = serverToEntityX.at(clientIndex);
entity.destroy();
serverToEntityX.erase(clientIndex);
std::cout << "client " << clientIndex << " disconnected" << std::endl;
}
@@ -110,7 +103,11 @@ void GameServer::ProcessMessages()
yojimbo::Message *message = m_server.ReceiveMessage(i, j);
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);
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:
ProcessTestMessage(clientIndex, (TestMessage *)message);
break;
default:
break;
m_server->ClientConnected(clientIndex);
}
}
void GameServer::ProcessTestMessage(int clientIndex, TestMessage *message)
void ServerAdapter::OnServerClientDisconnected(int clientIndex)
{
// ... process test message ...
if (m_server != NULL)
{
m_server->ClientDisconnected(clientIndex);
}
}

View File

@@ -1,98 +1,60 @@
#pragma once
#ifndef GAMESERVER_H
#define GAMESERVER_H
#include <yojimbo.h>
//Std
#include <string>
#include <map>
//Vendor
#include <yojimbo.h>
#include <entityx/entityx.h>
//Own
#include "Shared.h"
static const int MAX_PLAYERS = 64;
class GameServer;
// a simple test message
enum class GameMessageType
class ServerAdapter : public SharedAdapter
{
TEST,
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();
};
// the adapter
class GameAdapter : public yojimbo::Adapter
{
public:
explicit GameAdapter(GameServer *server = NULL) : m_server(server) {}
public:
explicit ServerAdapter(GameServer *server);
void OnServerClientConnected(int clientIndex) override;
void OnServerClientDisconnected(int clientIndex) override;
yojimbo::MessageFactory* CreateMessageFactory(yojimbo::Allocator& allocator) override;
private:
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
{
public:
public:
GameServer(const yojimbo::Address &address);
void ClientConnected(int clientIndex);
void ClientDisconnected(int clientIndex);
void ProcessCommand(std::string command);
void ProcessMessages();
void ProcessMessage(int clientIndex, yojimbo::Message *message);
void ProcessTestMessage(int clientIndex, TestMessage *message);
void Run();
void Update(float dt);
bool m_running;
private:
GameConnectionConfig m_connectionConfig;
yojimbo::Server m_server;
GameAdapter m_adapter;
entityx::EntityX ex;
std::map<int, entityx::Entity> serverToEntityX;
private:
double m_time;
GameConnectionConfig m_connectionConfig;
ServerAdapter m_adapter;
yojimbo::Server m_server;
};
#endif //GAMESERVER_H

87
src/Shared.h Normal file
View 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
View 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
View 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
View 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;
};

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

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