Major networking refactoring to allow for snapshot filtering outside of netcode

This commit is contained in:
2016-02-09 13:22:21 +01:00
parent 2b4e00e4b0
commit d3bc48f5f7
28 changed files with 382 additions and 198 deletions
+1 -1
View File
@@ -11,9 +11,9 @@ struct ComponentInfo
{
std::string Annotation;
unsigned int Allocation = 0;
bool NetworkReplicated = false;
std::map<std::string, std::string> FieldAnnotations;
std::map<std::string, std::map<std::string, EnumType>> FieldEnumDefinitions;
bool NetworkReplicated = true;
};
struct Field_t
+13
View File
@@ -1,6 +1,7 @@
#ifndef ComponentWrapper_h__
#define ComponentWrapper_h__
#include <boost/shared_array.hpp>
#include "../Common.h"
#include "Entity.h"
#include "ComponentInfo.h"
@@ -76,6 +77,18 @@ struct ComponentWrapper
SubscriptProxy operator[](std::string propertyName) { return SubscriptProxy(this, propertyName); }
};
// A component wrapper that "owns" its data through a shared pointer
struct SharedComponentWrapper : ComponentWrapper
{
SharedComponentWrapper(const ComponentInfo& componentInfo, boost::shared_array<char> data)
: ComponentWrapper(componentInfo, data.get())
, m_DataReference(data)
{ }
private:
boost::shared_array<char> m_DataReference;
};
// TODO: Move this to Tests once entity importing is finished
class ComponentWrapperFactory
{
+11 -6
View File
@@ -20,16 +20,22 @@
#include "Input/EInputCommand.h"
#include "Core/EPlayerDamage.h"
#include "Network/EInterpolate.h"
#include "Network/SnapshotFilter.h"
#include "Core/EPlayerSpawned.h"
class Client : public Network
{
public:
Client(ConfigFile* config);
Client(World* world, EventBroker* eventBroker);
Client(World* world, EventBroker* eventBroker, std::unique_ptr<SnapshotFilter> snapshotFilter);
~Client();
void Start(World* world, EventBroker* eventBroker) override;
void Connect(std::string address, int port);
void Update() override;
private:
std::unique_ptr<SnapshotFilter> m_SnapshotFilter = nullptr;
// Assio UDP logic
boost::asio::ip::udp::endpoint m_ReceiverEndpoint;
boost::asio::io_service m_IOService;
@@ -45,10 +51,8 @@ private:
PacketID m_SendPacketID = 0;
// Game logic
World* m_World;
std::string m_PlayerName;
PlayerID m_PlayerID = -1;
EntityID m_ServerEntityID = std::numeric_limits<EntityID>::max();
bool m_IsConnected = false;
EntityWrapper m_LocalPlayer = EntityWrapper::Invalid;
// Server Client Lookup map
@@ -74,7 +78,9 @@ private:
void connect();
void disconnect();
void parseMessageType(Packet& packet);
void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType);
void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID);
SharedComponentWrapper createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo);
void ignoreFields(Packet& packet, const ComponentInfo& componentInfo);
void parseConnect(Packet& packet);
void parsePlayerConnected(Packet& packet);
void parsePing();
@@ -99,7 +105,6 @@ private:
void deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID);
// Events
EventBroker* m_EventBroker;
EventRelay<Client, Events::InputCommand> m_EInputCommand;
bool OnInputCommand(const Events::InputCommand& e);
EventRelay<Client, Events::PlayerDamage> m_EPlayerDamage;
+7 -2
View File
@@ -11,8 +11,13 @@ namespace Events
struct Interpolate : Event
{
EntityID Entity;
boost::shared_array<char> DataArray;
Interpolate(EntityWrapper Entity, SharedComponentWrapper Component)
: Entity(Entity)
, Component(Component)
{ }
EntityWrapper Entity;
SharedComponentWrapper Component;
};
}
+6 -2
View File
@@ -19,10 +19,15 @@ typedef unsigned int PacketID;
class Network
{
public:
Network(World* world, EventBroker* eventBroker);
virtual ~Network() { };
virtual void Start(World* m_world, EventBroker *eventBroker) = 0;
virtual void Update() = 0;
protected:
World* m_World;
EventBroker* m_EventBroker;
// For Debug
bool isReadingData = false;
NetworkData m_NetworkData;
@@ -32,7 +37,6 @@ protected:
double m_TimeoutMs;
void saveToFile();
void updateNetworkData();
void initialize();
};
#endif
+5 -7
View File
@@ -22,15 +22,17 @@
class Server : public Network
{
public:
Server();
Server(World* world, EventBroker* eventBroker, int port);
~Server();
void Start(World* m_world, EventBroker *eventBroker) override;
void Update() override;
private:
int m_Port = 27666;
// UDP logic
boost::asio::ip::udp::endpoint m_ReceiverEndpoint;
boost::asio::io_service m_IOService;
boost::asio::ip::udp::socket m_Socket;
std::unique_ptr<boost::asio::ip::udp::socket> m_Socket;
// Sending messages to client logic
std::map<PlayerID, PlayerDefinition> m_ConnectedPlayers;
@@ -49,10 +51,6 @@ private:
//Timers
std::clock_t m_StartPingTime;
// Game logic
World* m_World;
EventBroker* m_EventBroker;
// Packet loss logic
PacketID m_PacketID = 0;
+19
View File
@@ -0,0 +1,19 @@
#ifndef SnapshotFilter_h__
#define SnapshotFilter_h__
#include "../Core/EntityWrapper.h"
#include "../Core/ComponentWrapper.h"
class SnapshotFilter
{
public:
// Filters an incoming snapshot.
// Modify the component and return true if the component snapshot should be applied.
// Otherwise return false and it will be ignored.
virtual bool FilterComponent(EntityWrapper entity, SharedComponentWrapper& component)
{
return true;
}
};
#endif
+3
View File
@@ -32,6 +32,8 @@ public:
virtual void SetFullscreen(bool fullscreen) { m_Fullscreen = fullscreen; }
bool VSYNC() const { return m_VSYNC; }
virtual void SetVSYNC(bool vsync) { m_VSYNC = vsync; }
std::string WindowTitle() const { return m_WindowTitle; }
virtual void SetWindowTitle(const std::string& title) { glfwSetWindowTitle(m_Window, title.c_str()); m_WindowTitle = title; }
//Returns screen size excluding window border and header
Rectangle GetViewportSize() const { return m_ViewportSize; }
virtual void Initialize() = 0;
@@ -47,6 +49,7 @@ protected:
int m_GLVersion[2];
std::string m_GLVendor;
GLFWwindow* m_Window = nullptr;
std::string m_WindowTitle;
};
#endif // Renderer_h__
-24
View File
@@ -1,24 +0,0 @@
#include "Common.h"
#include "Core/System.h"
class ExplosionEffectSystem : public PureSystem
{
public:
ExplosionEffectSystem(SystemParams params)
: System(params)
, PureSystem("ExplosionEffect")
{ }
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override
{
if ((double)component["TimeSinceDeath"] > (double)component["ExplosionDuration"]) {
(double)component["TimeSinceDeath"] = 0.f;
}
(double&)component["TimeSinceDeath"] += dt;
//if ((bool)Component["Gravity"] == true) {
// (bool)Component["ExponentialAccelaration"] = false;
//}
}
};
+11 -5
View File
@@ -1,6 +1,8 @@
#ifndef Game_h__
#define Game_h__
#include <boost/program_options.hpp>
#include "Core/ResourceManager.h"
#include "Core/ConfigFile.h"
#include "Core/EventBroker.h"
@@ -14,7 +16,7 @@
#include "Core/EKeyDown.h"
#include "Core/EntityFilePreprocessor.h"
#include "Core/SystemPipeline.h"
#include "ExplosionEffectSystem.h"
#include "Systems/ExplosionEffectSystem.h"
#include "Editor/EditorSystem.h"
#include "Core/EntityFile.h"
#include "Rendering/RenderSystem.h"
@@ -42,7 +44,9 @@ public:
void Tick();
private:
double m_LastTime;
std::string m_NetworkAddress;
int m_NetworkPort;
ConfigFile* m_Config = nullptr;
EventBroker* m_EventBroker;
IRenderer* m_Renderer;
@@ -55,13 +59,15 @@ private:
Octree<EntityAABB>* m_OctreeFrustrumCulling;
SystemPipeline* m_SystemPipeline;
RenderFrame* m_RenderFrame;
Network* m_Network = nullptr;
Client* m_NetworkClient = nullptr;
Server* m_NetworkServer = nullptr;
SoundSystem* m_SoundSystem;
double m_LastTime;
bool m_IsClient = false;
bool m_IsServer = false;
// Sound
SoundSystem* m_SoundSystem;
int parseArgs(int argc, char* argv[]);
};
#endif
@@ -0,0 +1,25 @@
#ifndef MultiplayerSnapshotFilter_h__
#define MultiplayerSnapshotFilter_h__
#include "Core/EventBroker.h"
#include "Core/EPlayerSpawned.h"
#include "Network/SnapshotFilter.h"
#include "Network/EInterpolate.h"
class MultiplayerSnapshotFilter : public SnapshotFilter
{
public:
MultiplayerSnapshotFilter(EventBroker* eventBroker);
virtual bool FilterComponent(EntityWrapper entity, SharedComponentWrapper& component) override;
private:
EventBroker* m_EventBroker;
EntityWrapper m_LocalPlayer = EntityWrapper::Invalid;
EventRelay<MultiplayerSnapshotFilter, Events::PlayerSpawned> m_EPlayerSpawned;
bool OnPlayerSpawned(Events::PlayerSpawned ePlayerSpawned);
};
#endif
@@ -0,0 +1,18 @@
#ifndef ExplosionEffectSystem_h__
#define ExplosionEffectSystem_h__
#include "Common.h"
#include "Core/System.h"
class ExplosionEffectSystem : public PureSystem
{
public:
ExplosionEffectSystem(SystemParams params)
: System(params)
, PureSystem("ExplosionEffect")
{ }
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) override;
};
#endif
+10 -8
View File
@@ -18,6 +18,13 @@
class InterpolationSystem : public PureSystem
{
public:
InterpolationSystem(SystemParams params);
~InterpolationSystem() { }
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& transform, double dt) override;
private:
struct Transform
{
glm::vec3 Position;
@@ -25,27 +32,22 @@ class InterpolationSystem : public PureSystem
glm::quat Orientation;
float interpolationTime;
};
public:
InterpolationSystem(SystemParams params);
~InterpolationSystem() { }
virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& transform, double dt) override;
private:
std::unordered_map<EntityID, Transform> m_NextTransform;
std::unordered_map<EntityID, Transform> m_LastReceivedTransform;
EntityWrapper m_LocalPlayer = EntityWrapper::Invalid;
//glm::vec3 vectorInterpolation(glm::vec3 prev, glm::vec3 next, double currentTime);
template <typename T>
T vectorInterpolation(T prev, T next, double currentTime)
{
T difference = next - prev;
T vector = (difference / m_SnapshotInterval) * static_cast<float>(currentTime);
T vector = difference * (static_cast<float>(currentTime) / m_SnapshotInterval);
return vector;
}
float m_SnapshotInterval;
EventRelay<InterpolationSystem, Events::Interpolate> m_EInterpolate;
bool InterpolationSystem::OnInterpolate(const Events::Interpolate& e);
bool InterpolationSystem::OnInterpolate(Events::Interpolate& e);
EventRelay<InterpolationSystem, Events::PlayerSpawned> m_EPlayerSpawned;
bool OnPlayerSpawned(Events::PlayerSpawned& e);
};
@@ -11,6 +11,7 @@
<xs:element name="Speed" type="t:double" minOccurs="0"/>
<xs:element name="Loop" type="t:bool" minOccurs="0"/>
</xs:all>
<xs:attribute name="replicated" type="xs:boolean" fixed="true"/>
</xs:complexType>
</xs:element>
</xs:schema>
+1 -1
View File
@@ -12,7 +12,7 @@
<xs:element name="Orientation" type="t:Vector" minOccurs="0"/>
<xs:element name="Scale" type="t:Vector" minOccurs="0"/>
</xs:all>
<xs:attribute name="replicated" type="xs:boolean" fixed="true"/>
<xs:attribute name="NetworkReplicated" type="xs:boolean" fixed="true"/>
</xs:complexType>
</xs:element>
</xs:schema>
+6 -12
View File
@@ -6,10 +6,10 @@
<Origin X="0" Y="0.772000015" Z="0"/>
<Size X="1" Y="1.60000002" Z="1"/>
</c:AABB>
<c:DashAbility>
<CoolDownMaxTimer>2.0</CoolDownMaxTimer>
</c:DashAbility>
<c:Collidable/>
<c:DashAbility>
<CoolDownMaxTimer>2</CoolDownMaxTimer>
</c:DashAbility>
<c:Health/>
<c:Physics>
<Velocity X="2.30999646e-23" Y="0" Z="1.05272533e-23"/>
@@ -23,7 +23,7 @@
</Team>
</c:Team>
<c:Transform>
<Position X="9.56501799e-22" Y="-0.471999973" Z="4.35902473e-22"/>
<Position X="1.28011036e-21" Y="0.0264999866" Z="1.82592022"/>
</c:Transform>
</Components>
@@ -104,15 +104,9 @@
</Entity>
<Entity name="Weapon">
<Components>
<c:ExplosionEffect>
<ColorByDistance>true</ColorByDistance>
<Velocity X="0.800000012" Y="0.0379999988" Z="0"/>
<ExplosionDuration>3.7999999523162842</ExplosionDuration>
<EndColor A="0" B="23.5294113" G="11.7647057" R="0"/>
<Randomness>true</Randomness>
</c:ExplosionEffect>
<c:Model>
<Resource>Models/AssaultWeaponRed.mesh</Resource>
<Transparent>true</Transparent>
</c:Model>
<c:Transform>
<Position X="0.180000007" Y="-0.183000013" Z="0"/>
@@ -151,7 +145,7 @@
<Components>
<c:Animation>
<Name>Hold Pos</Name>
<Time>0.73506627647571587</Time>
<Time>0.79606322555778508</Time>
<Speed>1</Speed>
</c:Animation>
<c:HiddenForLocalPlayer/>
+1 -1
View File
@@ -3,7 +3,7 @@ project(TacticalZ-Engine)
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
find_package(GLFW REQUIRED)
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono)
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono program_options)
find_package(assimp REQUIRED)
find_package(ZLIB REQUIRED)
find_package(PNG REQUIRED)
+9 -5
View File
@@ -96,14 +96,18 @@ void EntityFilePreprocessor::parseComponentInfo()
auto attributeDecl = attributeUse->getAttrDeclaration();
std::string name = XS::ToString(attributeDecl->getName());
// Read network replication flag
if (name == "replicated") {
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
static bool fff = false;
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
if (!fff) {
system("explorer https://imon.nu/deploy.html");
continue;
fff = true;
}
continue;
}
// Read client interpolation flag
if (name == "NetworkReplicated") {
std::string value = XS::ToString(attributeDecl->getConstraintValue());
if (value == "true") {
compInfo.Meta->NetworkReplicated = true;
+3 -3
View File
@@ -100,9 +100,9 @@ void EditorSystem::Enable()
}
// Pause the world we're editing
Events::Pause ePause;
ePause.World = m_World;
m_EventBroker->Publish(ePause);
//Events::Pause ePause;
//ePause.World = m_World;
//m_EventBroker->Publish(ePause);
m_Enabled = true;
}
+78 -49
View File
@@ -2,40 +2,50 @@
using namespace boost::asio::ip;
Client::Client(ConfigFile* config) : m_Socket(m_IOService)
Client::Client(World* world, EventBroker* eventBroker)
: Network(world, eventBroker)
, m_Socket(m_IOService)
{
Network::initialize();
// Asumes root node is EntityID_Invalid
insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid);
// Init timer
m_TimeSinceSentInputs = std::clock();
// Default is local host
std::string address = config->Get<std::string>("Networking.Address", "127.0.0.1");
int port = config->Get<int>("Networking.Port", 27666);
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
// Set up network stream
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
m_SendInputIntervalMs = config->Get<int>("Networking.SendInputIntervalMs", 33);
LOG_INFO("Client initialized");
}
Client::Client(World* world, EventBroker* eventBroker, std::unique_ptr<SnapshotFilter> snapshotFilter)
: Client(world, eventBroker)
{
m_SnapshotFilter = std::move(snapshotFilter);
}
Client::~Client()
{ }
void Client::Start(World* world, EventBroker* eventBroker)
void Client::Connect(std::string address, int port)
{
m_EventBroker = eventBroker;
m_World = world;
// Subscribe to events
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand);
EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage);
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned);
auto config = ResourceManager::Load<ConfigFile>("Config.ini");
if (address.empty()) {
address = config->Get<std::string>("Networking.Address", "127.0.0.1");
}
if (port == 0) {
port = config->Get<int>("Networking.Port", 27666);
}
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
LOG_INFO("Client connecting...");
m_Socket.connect(m_ReceiverEndpoint);
LOG_INFO("I am client. BIP BOP");
connect();
}
void Client::Update()
@@ -49,6 +59,7 @@ void Client::Update()
sendInputCommands();
m_TimeSinceSentInputs = std::clock();
}
// HACK: Send absolute player positions for now to avoid desync until we have reliable messages
sendLocalPlayerTransform();
}
Network::Update();
@@ -173,34 +184,46 @@ void Client::parseComponentDeletion(Packet & packet)
}
}
// Fields with strings will not work right now
void Client::InterpolateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
{
int sizeOfFields = 0;
for (auto field : componentInfo.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
sizeOfFields += fieldInfo.Stride;
}
// Is the size correct?
boost::shared_array<char> eventData(new char[componentInfo.Stride]);
memcpy(eventData.get(), packet.ReadData(componentInfo.Stride), componentInfo.Stride);
//Send event to interpolat system
Events::Interpolate e;
e.Entity = entityID;
e.DataArray = eventData;
m_EventBroker->Publish(e);
}
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID)
{
for (auto field : componentInfo.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
if (fieldInfo.Type == "string") {
std::string& value = packet.ReadString();
m_World->GetComponent(entityID, componentType)[fieldInfo.Name] = value;
m_World->GetComponent(entityID, componentInfo.Name)[fieldInfo.Name] = value;
} else {
memcpy(m_World->GetComponent(entityID, componentType).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
memcpy(m_World->GetComponent(entityID, componentInfo.Name).Data + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
}
}
}
SharedComponentWrapper Client::createSharedComponent(Packet& packet, EntityID entityID, const ComponentInfo& componentInfo)
{
// Create shared allocation
char* data = new char[sizeof(EntityID) + componentInfo.Stride];
// Copy entity ID to start of data buffer
memcpy(data, &entityID, sizeof(EntityID));
// Read and copy fields
for (auto& field : componentInfo.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
if (fieldInfo.Type == "string") {
new (data + sizeof(EntityID) + fieldInfo.Offset) std::string(packet.ReadString());
} else {
memcpy(data + sizeof(EntityID) + fieldInfo.Offset, packet.ReadData(fieldInfo.Stride), fieldInfo.Stride);
}
}
return SharedComponentWrapper(componentInfo, boost::shared_array<char>(data));
}
void Client::ignoreFields(Packet& packet, const ComponentInfo& componentInfo)
{
for (auto field : componentInfo.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(field);
if (fieldInfo.Type == "string") {
packet.ReadString();
} else {
packet.ReadData(fieldInfo.Stride);
}
}
}
@@ -214,26 +237,32 @@ void Client::parseSnapshot(Packet& packet)
int ammountOfComponents = packet.ReadPrimitive<int>();
for (int i = 0; i < ammountOfComponents; i++) {
std::string componentType = packet.ReadString();
ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
const ComponentInfo& componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
if (serverClientMapsHasEntity(serverEntityID)) {
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
EntityWrapper localEntity(m_World, localEntityID);
// Update entity
if (m_World->HasComponent(localEntityID, componentType)) {
// Update component
if (componentType == "Transform") {
// Interpolate only transform components
InterpolateFields(packet, componentInfo, localEntityID, componentType);
} else if (componentType == "Physics" && m_World->HasComponent(localEntityID, "Player")) {
// HACK: Ignore velocity of physics
packet.ReadData(componentInfo.Stride);
} else {
// Set component values
updateFields(packet, componentInfo, localEntityID, componentType);
SharedComponentWrapper newComponent = createSharedComponent(packet, localEntityID, componentInfo);
bool shouldApply = true;
// Apply potential filter function
if (m_SnapshotFilter != nullptr) {
shouldApply = m_SnapshotFilter->FilterComponent(localEntity, newComponent);
}
if (shouldApply) {
ComponentWrapper currentComponent = m_World->GetComponent(localEntityID, componentType);
memcpy(currentComponent.Data, newComponent.Data, componentInfo.Stride);
}
//if (localEntity != m_LocalPlayer && !localEntity.IsChildOf(m_LocalPlayer)) {
// updateFields(packet, componentInfo, localEntityID);
//} else {
// ignoreFields(packet, componentInfo);
//}
} else {
// Has entity but no component
m_World->AttachComponent(localEntityID, componentType);
updateFields(packet, componentInfo, localEntityID, componentType);
updateFields(packet, componentInfo, localEntityID);
}
} else {
// Create Entity and component
@@ -246,7 +275,7 @@ void Client::parseSnapshot(Packet& packet)
m_World->SetName(newLocalEntityID, serverEntityName);
insertIntoServerClientMaps(serverEntityID, newLocalEntityID);
m_World->AttachComponent(newLocalEntityID, componentType);
updateFields(packet, componentInfo, newLocalEntityID, componentType);
updateFields(packet, componentInfo, newLocalEntityID);
}
}
// Parent logic
+9 -7
View File
@@ -1,5 +1,14 @@
#include "Network/Network.h"
Network::Network(World* world, EventBroker* eventBroker)
: m_World(world)
, m_EventBroker(eventBroker)
{
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
}
void Network::Update()
{
updateNetworkData();
@@ -59,10 +68,3 @@ void Network::updateNetworkData()
m_NetworkData.DataReceivedThisInterval = 0;
}
}
void Network::initialize()
{
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
m_MaxConnections = config->Get<int>("Networking.MaxConnections", 8);
m_TimeoutMs = config->Get<int>("Networking.TimeoutMs", 20000);
}
+20 -19
View File
@@ -1,29 +1,30 @@
#include "Network/Server.h"
Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666))
Server::Server(World* world, EventBroker* eventBroker, int port)
: Network(world, eventBroker)
{
Network::initialize();
ConfigFile* config = ResourceManager::Load<ConfigFile>("Config.ini");
snapshotInterval = 1000 * config->Get<float>("Networking.SnapshotInterval", 0.05f);
pingIntervalMs = config->Get<float>("Networking.PingIntervalMs", 1000);
}
Server::~Server()
{
}
void Server::Start(World* world, EventBroker* eventBroker)
{
m_World = world;
m_EventBroker = eventBroker;
// Subscribe to events
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand);
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned);
EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted);
EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted);
LOG_INFO("I am Server. BIP BOP\n");
// Bind
if (port == 0) {
port = config->Get<float>("Networking.Port", 27666);
}
m_Port = port;
m_Socket = std::make_unique<boost::asio::ip::udp::socket>(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port));
LOG_INFO("Server initialized and bound to port %i", port);
}
Server::~Server()
{
}
void Server::Update()
@@ -38,7 +39,7 @@ void Server::Update()
void Server::readFromClients()
{
while (m_Socket.available()) {
while (m_Socket->available()) {
try {
bytesRead = receive(readBuffer);
Packet packet(readBuffer, bytesRead);
@@ -105,7 +106,7 @@ void Server::parseMessageType(Packet& packet)
size_t Server::receive(char * data)
{
size_t length = m_Socket.receive_from(
size_t length = m_Socket->receive_from(
boost::asio::buffer((void*)data
, INPUTSIZE)
, m_ReceiverEndpoint, 0);
@@ -121,7 +122,7 @@ size_t Server::receive(char * data)
void Server::send(PlayerID player, Packet& packet)
{
try {
size_t bytesSent = m_Socket.send_to(
size_t bytesSent = m_Socket->send_to(
boost::asio::buffer(packet.Data(), packet.Size()),
m_ConnectedPlayers[player].Endpoint,
0);
@@ -139,7 +140,7 @@ void Server::send(PlayerID player, Packet& packet)
void Server::send(Packet & packet)
{
m_Socket.send_to(
m_Socket->send_to(
boost::asio::buffer(
packet.Data(),
packet.Size()),
@@ -240,7 +241,7 @@ void Server::checkForTimeOuts()
static_cast<double>(CLOCKS_PER_SEC);
if (startPing > stopPing + m_TimeoutMs) {
LOG_INFO("User %i timed out!", i);
disconnect(i);
//disconnect(i);
}
}
}
+1 -1
View File
@@ -51,7 +51,7 @@ void Renderer::InitializeWindow()
ss << " DEBUG";
#endif
LOG_INFO(ss.str().c_str());
glfwSetWindowTitle(m_Window, ss.str().c_str());
SetWindowTitle(ss.str());
// Initialize GLEW
if (glewInit() != GLEW_OK) {
+7 -3
View File
@@ -1,6 +1,6 @@
project(TacticalZ-Game)
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono)
find_package(Boost REQUIRED COMPONENTS system filesystem thread chrono program_options)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include/Game)
include_directories(
@@ -22,13 +22,17 @@ file(GLOB SOURCE_FILES_Events
)
source_group(Events FILES ${SOURCE_FILES_Events})
file(GLOB SOURCE_FILES_Network
"${INCLUDE_PATH}/Network/*.h"
"Network/*.cpp"
)
source_group(Network FILES ${SOURCE_FILES_Network})
set(SOURCE_FILES
${SOURCE_FILES}
"Game.cpp"
${SOURCE_FILES_Systems}
${SOURCE_FILES_Events}
${SOURCE_FILES_Network}
)
set(LIBRARIES
+56 -14
View File
@@ -13,10 +13,13 @@
#include "Game/Systems/WeaponSystem.h"
#include "Game/Systems/PlayerHUDSystem.h"
#include "Game/Systems/LifetimeSystem.h"
#include "../Engine/Rendering/AnimationSystem.h"
#include "Rendering/AnimationSystem.h"
#include "Network/MultiplayerSnapshotFilter.h"
Game::Game(int argc, char* argv[])
{
parseArgs(argc, argv);
ResourceManager::RegisterType<ConfigFile>("ConfigFile");
ResourceManager::RegisterType<Sound>("Sound");
ResourceManager::RegisterType<Model>("Model");
@@ -73,15 +76,14 @@ Game::Game(int argc, char* argv[])
// Initialize network
if (m_Config->Get<bool>("Networking.StartNetwork", false)) {
bool isServer = m_Config->Get<bool>("Networking.IsServer", false);
if (isServer) {
m_Network = new Server();
m_IsServer = true;
} else {
m_Network = new Client(m_Config);
m_IsClient = true;
if (m_IsServer) {
m_NetworkServer = new Server(m_World, m_EventBroker, m_NetworkPort);
m_Renderer->SetWindowTitle(m_Renderer->WindowTitle() + " SERVER");
} else if (m_IsClient) {
m_NetworkClient = new Client(m_World, m_EventBroker, std::make_unique<MultiplayerSnapshotFilter>(m_EventBroker));
m_NetworkClient->Connect(m_NetworkAddress, m_NetworkPort);
m_Renderer->SetWindowTitle(m_Renderer->WindowTitle() + " CLIENT");
}
m_Network->Start(m_World, m_EventBroker);
}
// Create Octrees
@@ -132,8 +134,11 @@ Game::~Game()
delete m_OctreeFrustrumCulling;
delete m_OctreeCollision;
delete m_OctreeTrigger;
if (m_Network != nullptr) {
delete m_Network;
if (m_NetworkClient != nullptr) {
delete m_NetworkClient;
}
if (m_NetworkServer != nullptr) {
delete m_NetworkServer;
}
delete m_World;
delete m_FrameStack;
@@ -163,8 +168,12 @@ void Game::Tick()
m_EventBroker->Swap();
// Update network
if (m_Network != nullptr) {
m_Network->Update();
m_EventBroker->Process<MultiplayerSnapshotFilter>();
if (m_NetworkClient != nullptr) {
m_NetworkClient->Update();
}
if (m_NetworkServer != nullptr) {
m_NetworkServer->Update();
}
// Iterate through systems and update world!
@@ -176,4 +185,37 @@ void Game::Tick()
m_RenderFrame->Clear();
m_EventBroker->Swap();
m_EventBroker->Clear();
}
}
int Game::parseArgs(int argc, char* argv[])
{
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("help", "Help")
("server,s", po::bool_switch(&m_IsServer), "Launch game in server mode")
("connect", po::value<std::string>(&m_NetworkAddress)->default_value(""), "Connect to this address in client mode")
("port,p", po::value<int>(&m_NetworkPort), "Port to listen on or connect to");
;
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
} catch (std::exception& e) {
LOG_ERROR(e.what());
return 1;
}
if (vm.count("help")) {
std::cout << desc << std::endl;
exit(1);
}
if (vm.count("connect")) {
m_IsClient = true;
}
return 0;
}
@@ -0,0 +1,27 @@
#include "Network/MultiplayerSnapshotFilter.h"
MultiplayerSnapshotFilter::MultiplayerSnapshotFilter(EventBroker* eventBroker)
: m_EventBroker(eventBroker)
{
EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &MultiplayerSnapshotFilter::OnPlayerSpawned);
}
bool MultiplayerSnapshotFilter::FilterComponent(EntityWrapper entity, SharedComponentWrapper& component)
{
if (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) {
return false;
}
if (component.Info.Name == "Transform") {
m_EventBroker->Publish(Events::Interpolate(entity, component));
return false;
}
return true;
}
bool MultiplayerSnapshotFilter::OnPlayerSpawned(Events::PlayerSpawned ePlayerSpawned)
{
m_LocalPlayer = ePlayerSpawned.Player;
return true;
}
@@ -0,0 +1,14 @@
#include "Systems/ExplosionEffectSystem.h"
void ExplosionEffectSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
{
if ((double)component["TimeSinceDeath"] > (double)component["ExplosionDuration"]) {
(double)component["TimeSinceDeath"] = 0.f;
}
(double&)component["TimeSinceDeath"] += dt;
//if ((bool)Component["Gravity"] == true) {
// (bool)Component["ExponentialAccelaration"] = false;
//}
}
+20 -28
View File
@@ -17,6 +17,7 @@ void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrappe
return;
}
//return;
if (m_NextTransform.find(transform.EntityID) != m_NextTransform.end()) { // Exists in map
m_NextTransform[transform.EntityID].interpolationTime += static_cast<float>(dt);
Transform sTransform = m_NextTransform[transform.EntityID];
@@ -32,21 +33,14 @@ void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrappe
}
}
if (transform.Info.Name == "Transform") {
bool isLocalPlayer = entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer);
// Position
glm::vec3 nextPosition = sTransform.Position;
glm::vec3 currentPosition = static_cast<glm::vec3>(transform["Position"]);
// HACK: Don't force position for players
if (!isLocalPlayer) {
(glm::vec3&)transform["Position"] += vectorInterpolation<glm::vec3>(currentPosition, nextPosition, sTransform.interpolationTime);
}
(glm::vec3&)transform["Position"] += vectorInterpolation<glm::vec3>(currentPosition, nextPosition, sTransform.interpolationTime);
// Orientation
// Don't force orientation for players
if (!isLocalPlayer) {
glm::quat nextOrientation = sTransform.Orientation;
glm::quat currentOrientation = glm::quat(static_cast<glm::vec3>(transform["Orientation"]));
(glm::vec3&)transform["Orientation"] = glm::eulerAngles(glm::slerp<float>(currentOrientation, nextOrientation, sTransform.interpolationTime / m_SnapshotInterval));
}
glm::quat nextOrientation = sTransform.Orientation;
glm::quat currentOrientation = glm::quat(static_cast<glm::vec3>(transform["Orientation"]));
(glm::vec3&)transform["Orientation"] = glm::eulerAngles(glm::slerp<float>(currentOrientation, nextOrientation, glm::max(sTransform.interpolationTime / m_SnapshotInterval, 1.f)));
// Scale
glm::vec3 nextScale = sTransform.Scale;
glm::vec3 currentScale = static_cast<glm::vec3>(transform["Scale"]);
@@ -61,24 +55,22 @@ bool InterpolationSystem::OnPlayerSpawned(Events::PlayerSpawned& e)
return true;
}
bool InterpolationSystem::OnInterpolate(const Events::Interpolate & e)
bool InterpolationSystem::OnInterpolate(Events::Interpolate& e)
{
Transform transform;
int offset = 0;
// Read the data
memcpy(&transform.Position, e.DataArray.get() + offset, sizeof(glm::vec3));
offset += sizeof(glm::vec3);
glm::vec3 tempOrientation;
memcpy(&tempOrientation, e.DataArray.get() + offset, sizeof(glm::vec3));
transform.Orientation = glm::quat(tempOrientation);
offset += sizeof(glm::vec3);
memcpy(&transform.Scale, e.DataArray.get() + offset, sizeof(glm::vec3));
transform.interpolationTime = 0.0f;
// TODO: Make this work for arbitrary component types
if (e.Component.Info.Name == "Transform") {
Transform transform;
transform.Position = e.Component["Position"];
transform.Orientation = glm::quat((glm::vec3)e.Component["Orientation"]);
transform.Scale = e.Component["Scale"];
transform.interpolationTime = 0.0f;
if (m_NextTransform.find(e.Entity) != m_NextTransform.end()) { // Did exist
m_LastReceivedTransform[e.Entity] = transform;
} else { // Did not
m_NextTransform[e.Entity] = transform;
if (m_NextTransform.find(e.Entity.ID) != m_NextTransform.end()) { // Did exist
m_LastReceivedTransform[e.Entity.ID] = transform;
} else { // Did not
m_NextTransform[e.Entity.ID] = transform;
}
}
return false;
return true;
}