Merge remote-tracking branch 'origin/Networking'

# Conflicts:
#	src/Engine/Network/Client.cpp
#	src/Game/Systems/WeaponSystem.cpp
This commit is contained in:
2016-01-24 17:18:39 +01:00
13 changed files with 280 additions and 124 deletions
+21
View File
@@ -0,0 +1,21 @@
#ifndef EComponentDeleted_h__
#define EComponentDeleted_h__
#include "../Common.h"
#include "Event.h"
#include "Entity.h"
namespace Events
{
struct ComponentDeleted : Event
{
EntityID Entity;
std::string ComponentType;
// True if the component was deleted as a result of the entity it was attached to being deleted
bool Cascaded;
};
}
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef EEntityDeleted_h__
#define EEntityDeleted_h__
#include "Event.h"
#include "Entity.h"
namespace Events
{
struct EntityDeleted : Event
{
EntityID DeletedEntity;
// True if the entity deletion was triggered because the entity's parent was deleted before it
bool Cascaded;
};
}
#endif
+6
View File
@@ -5,11 +5,15 @@
#include "Entity.h"
#include "ObjectPool.h"
#include "ComponentPool.h"
#include "EventBroker.h"
class World
{
public:
World() = default;
World(EventBroker* eventBroker)
: m_EventBroker(eventBroker)
{ }
~World();
// Create empty entity
@@ -46,6 +50,7 @@ public:
std::string GetName(EntityID entity) const;
private:
EventBroker* m_EventBroker = nullptr;
EntityID m_CurrentEntityID = 0;
std::unordered_map<EntityID, EntityID> m_EntityParents;
@@ -55,6 +60,7 @@ private:
std::unordered_map<EntityID, std::string> m_EntityNames;
EntityID generateEntityID();
void deleteEntityRecursive(EntityID entity, bool cascaded = false);
};
#endif
+3
View File
@@ -79,6 +79,8 @@ private:
void parsePing();
void parseKick();
void parsePlayersSpawned(Packet& packet);
void parseEntityDeletion(Packet& packet);
void parseComponentDeletion(Packet& packet);
void InterpolateFields(Packet & packet, const ComponentInfo & componentInfo, const EntityID & entityID, const std::string & componentType);
void parseSnapshot(Packet& packet);
void identifyPacketLoss();
@@ -92,6 +94,7 @@ private:
// Returns if server EntityID exist in map
bool serverClientMapsHasEntity(EntityID serverEntityID);
void insertIntoServerClientMaps(EntityID serverEntityID, EntityID clientEntityID);
void deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID);
// Events
EventBroker* m_EventBroker;
+3 -1
View File
@@ -15,7 +15,9 @@ enum class MessageType
PlayerConnected,
BecomePlayer,
Kick,
OnPlayerSpawned
OnPlayerSpawned,
EntityDeleted,
ComponentDeleted
};
#endif
+7
View File
@@ -16,6 +16,8 @@
#include "Core/EPlayerDamage.h"
#include "Network/EPlayerDisconnected.h"
#include "Core/EPlayerSpawned.h"
#include "Core/EEntityDeleted.h"
#include "Core/EComponentDeleted.h"
class Server : public Network
{
@@ -63,6 +65,7 @@ private:
void send(Packet& packet);
void broadcast(Packet& packet);
void sendSnapshot();
void addChildrenToPacket(Packet& packet, EntityID entityID);
void sendPing();
void checkForTimeOuts();
void disconnect(UserID user);
@@ -82,6 +85,10 @@ private:
bool OnInputCommand(const Events::InputCommand& e);
EventRelay<Server, Events::PlayerSpawned> m_EPlayerSpawned;
bool OnPlayerSpawned(const Events::PlayerSpawned& e);
EventRelay<Server, Events::EntityDeleted> m_EEntityDeleted;
bool OnEntityDeleted(const Events::EntityDeleted& e);
EventRelay<Server, Events::ComponentDeleted> m_EComponentDeleted;
bool OnComponentDeleted(const Events::ComponentDeleted& e);
};
#endif
+2 -1
View File
@@ -21,4 +21,5 @@ F1=ToggleEditor
X=EditorToggleTransformSpace
C=ConnectToServer
N=SwitchToServer
M=SwitchToClient
M=SwitchToClient
P=SwitchToPlayer
+61 -32
View File
@@ -1,4 +1,6 @@
#include "Core/World.h"
#include "Core/EEntityDeleted.h"
#include "Core/EComponentDeleted.h"
World::~World()
{
@@ -21,37 +23,7 @@ EntityID World::CreateEntity(EntityID parent /*= 0*/)
void World::DeleteEntity(EntityID entity)
{
// Delete components
for (auto& pair : m_ComponentPools) {
auto& pool = pair.second;
if (pool->KnowsEntity(entity)) {
auto& c = pool->GetByEntity(entity);
pool->Delete(c);
}
}
// Loop through children
std::vector<EntityID> childrenToDelete;
auto children = m_EntityChildren.equal_range(entity);
for (auto it = children.first; it != children.second; ++it) {
childrenToDelete.push_back(it->second);
}
for (auto& child : childrenToDelete) {
DeleteEntity(child);
}
EntityID parent = m_EntityParents.at(entity);
m_EntityParents.erase(entity);
auto parentChildren = m_EntityChildren.equal_range(parent);
for (auto it = parentChildren.first; it != parentChildren.second; ++it) {
if (it->second == entity) {
m_EntityChildren.erase(it);
break;
}
}
// Erase potential name
m_EntityNames.erase(entity);
deleteEntityRecursive(entity, false);
}
bool World::ValidEntity(EntityID entity) const
@@ -96,7 +68,15 @@ void World::DeleteComponent(EntityID entity, const std::string& componentType)
{
ComponentPool* pool = m_ComponentPools.at(componentType);
ComponentWrapper c = pool->GetByEntity(entity);
return pool->Delete(c);
pool->Delete(c);
if (m_EventBroker != nullptr) {
Events::ComponentDeleted e;
e.Entity = entity;
e.ComponentType = componentType;
e.Cascaded = false;
m_EventBroker->Publish(e);
}
}
const ComponentPool* World::GetComponents(const std::string& componentType)
@@ -155,3 +135,52 @@ EntityID World::generateEntityID()
return m_CurrentEntityID++;
}
void World::deleteEntityRecursive(EntityID entity, bool cascaded /*= false*/)
{
if (m_EventBroker != nullptr) {
Events::EntityDeleted e;
e.DeletedEntity = entity;
e.Cascaded = cascaded;
m_EventBroker->Publish(e);
}
// Delete components
for (auto& pair : m_ComponentPools) {
auto& pool = pair.second;
if (pool->KnowsEntity(entity)) {
auto& c = pool->GetByEntity(entity);
pool->Delete(c);
if (m_EventBroker != nullptr) {
Events::ComponentDeleted e;
e.Entity = entity;
e.ComponentType = pair.first;
e.Cascaded = true;
m_EventBroker->Publish(e);
}
}
}
// Loop through children
std::vector<EntityID> childrenToDelete;
auto children = m_EntityChildren.equal_range(entity);
for (auto it = children.first; it != children.second; ++it) {
childrenToDelete.push_back(it->second);
}
for (auto& child : childrenToDelete) {
deleteEntityRecursive(child, true);
}
EntityID parent = m_EntityParents.at(entity);
m_EntityParents.erase(entity);
auto parentChildren = m_EntityChildren.equal_range(parent);
for (auto it = parentChildren.first; it != parentChildren.second; ++it) {
if (it->second == entity) {
m_EntityChildren.erase(it);
break;
}
}
// Erase potential name
m_EntityNames.erase(entity);
}
+91 -62
View File
@@ -7,13 +7,13 @@ Client::Client(ConfigFile* config) : m_Socket(m_IOService)
{
Network::initialize();
// Asumes root node is EntityID 0
insertIntoServerClientMaps(0, 0);
// 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", 13);
int port = config->Get<int>("Networking.Port", 27666);
m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port);
// Set up network stream
m_PlayerName = config->Get<std::string>("Networking.Name", "Raptorcopter");
@@ -96,6 +96,12 @@ void Client::parseMessageType(Packet& packet)
case MessageType::OnPlayerSpawned:
parsePlayersSpawned(packet);
break;
case MessageType::EntityDeleted:
parseEntityDeletion(packet);
break;
case MessageType::ComponentDeleted:
parseComponentDeletion(packet);
break;
default:
break;
}
@@ -128,13 +134,13 @@ void Client::parsePing()
}
void Client::parseKick()
{
{
LOG_WARNING("You have been kicked from the server.");
m_IsConnected = false;
}
void Client::parsePlayersSpawned(Packet& packet)
{
{
Events::PlayerSpawned e;
e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive<EntityID>()]);
@@ -142,6 +148,25 @@ void Client::parsePlayersSpawned(Packet& packet)
m_EventBroker->Publish(e);
}
void Client::parseEntityDeletion(Packet & packet)
{
EntityID entityToDelete = packet.ReadPrimitive<EntityID>();
EntityID localEntity = m_ServerIDToClientID.at(entityToDelete);
if (m_World->ValidEntity(localEntity)) {
m_World->DeleteEntity(localEntity);
deleteFromServerClientMaps(entityToDelete, localEntity);
}
}
void Client::parseComponentDeletion(Packet & packet)
{
EntityID entity = packet.ReadPrimitive<EntityID>();
std::string componentType = packet.ReadString();
if (m_World->HasComponent(entity, componentType)) {
m_World->DeleteComponent(m_ServerIDToClientID.at(entity), componentType);
}
}
// Fields with strings will not work right now
void Client::InterpolateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType)
{
@@ -176,66 +201,50 @@ void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, co
void Client::parseSnapshot(Packet& packet)
{
std::string componentType = packet.ReadString();
while (packet.DataReadSize() < packet.Size()) {
// HACK
std::string entityName = packet.ReadString();
// Components EntityID
EntityID receivedEntityID = packet.ReadPrimitive<EntityID>();
// HACK
m_World->SetName(receivedEntityID, entityName);
// Parents EntityID
EntityID receivedParentEntityID = packet.ReadPrimitive<EntityID>();
ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
// Check if the received EntityID is mapped to one of our local EntityIDs
if (serverClientMapsHasEntity(receivedEntityID)) {
// Get the local EntityID
EntityID entityID = m_ServerIDToClientID.at(receivedEntityID);
// Check if the component exists
if (m_World->HasComponent(entityID, componentType)) {
// If the entity and the component exists update it
if (componentType == "Transform") {
InterpolateFields(packet, componentInfo, entityID, componentType);
EntityID serverEntityID = packet.ReadPrimitive<EntityID>();
EntityID serverParentID = packet.ReadPrimitive<EntityID>();
std::string serverEntityName = packet.ReadString();
int ammountOfComponents = packet.ReadPrimitive<int>();
for (int i = 0; i < ammountOfComponents; i++) {
std::string componentType = packet.ReadString();
ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo();
if (serverClientMapsHasEntity(serverEntityID)) {
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
// Update entity
if (m_World->HasComponent(localEntityID, componentType)) {
// Update component
if (componentType == "Transform") {
// Interpolate only transform components
InterpolateFields(packet, componentInfo, localEntityID, componentType);
} else {
// Set component values
updateFields(packet, componentInfo, localEntityID, componentType);
}
} else {
updateFields(packet, componentInfo, entityID, componentType);
// Has entity but no component
m_World->AttachComponent(localEntityID, componentType);
updateFields(packet, componentInfo, localEntityID, componentType);
}
// if entity exists but not the component
} else {
// Create component
m_World->AttachComponent(entityID, componentType);
// Copy data to newly created component
updateFields(packet, componentInfo, entityID, componentType);
// Create Entity and component
EntityID newLocalEntityID;
if (serverParentID == EntityID_Invalid) {
newLocalEntityID = m_World->CreateEntity(EntityID_Invalid);
} else {
newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID));
}
m_World->SetName(newLocalEntityID, serverEntityName);
insertIntoServerClientMaps(serverEntityID, newLocalEntityID);
m_World->AttachComponent(newLocalEntityID, componentType);
updateFields(packet, componentInfo, newLocalEntityID, componentType);
}
// If the entity dosent exist nor the component
} else {
// Create Entity
// If entity dosen't exist
EntityID newEntityID = m_World->CreateEntity();
insertIntoServerClientMaps(receivedEntityID, newEntityID);
// Check if EntityIDs are out of sync
if (newEntityID != receivedEntityID) {
LOG_INFO("Client::parseSnapshot(Packet& packet): Newly created EntityID is not the \
same as the one sent by server (EntityIDs are out of sync)");
}
// Create component
m_World->AttachComponent(newEntityID, componentType);
// Copy data to newly created component
updateFields(packet, componentInfo, newEntityID, componentType);
}
// Parent Logic
// Don't need to check if receivedEntityID is mapped. (It should have been set)
if (receivedParentEntityID != std::numeric_limits<EntityID>::max()) {
if (serverClientMapsHasEntity(receivedParentEntityID)) {
m_World->SetParent(m_ServerIDToClientID.at(receivedEntityID), m_ServerIDToClientID.at(receivedParentEntityID));
// If Parent dosen't exist create one and map receivedParentEntityID to it.
} else {
// Create the new parent and add it to map
EntityID newParentEntityID = m_World->CreateEntity();
insertIntoServerClientMaps(receivedParentEntityID, newParentEntityID);
// Set the newly created Entity as parent.
m_World->SetParent(m_ServerIDToClientID.at(receivedEntityID), newParentEntityID);
}
// Parent logic
// This should be enough beacause we know that the entities arives in pre-order (there will always be a parent)
if (serverParentID != EntityID_Invalid) {
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID));
}
}
}
@@ -326,7 +335,7 @@ bool Client::OnInputCommand(const Events::InputCommand & e)
bool Client::OnPlayerDamage(const Events::PlayerDamage & e)
{
Packet packet(MessageType::OnInputCommand, m_SendPacketID);
Packet packet(MessageType::OnPlayerDamage, m_SendPacketID);
packet.WritePrimitive(e.Damage);
packet.WritePrimitive(e.Player.ID);
send(packet);
@@ -386,12 +395,26 @@ void Client::becomePlayer()
bool Client::clientServerMapsHasEntity(EntityID clientEntityID)
{
return m_ClientIDToServerID.find(clientEntityID) != m_ClientIDToServerID.end();
if (m_ClientIDToServerID.find(clientEntityID) != m_ClientIDToServerID.end()) {
if (m_World->ValidEntity(clientEntityID)) {
return true;
}
EntityID serverEntityID = m_ClientIDToServerID.at(clientEntityID);
deleteFromServerClientMaps(serverEntityID, clientEntityID);
}
return false;
}
bool Client::serverClientMapsHasEntity(EntityID serverEntityID)
{
return m_ServerIDToClientID.find(serverEntityID) != m_ServerIDToClientID.end();
if (m_ServerIDToClientID.find(serverEntityID) != m_ServerIDToClientID.end()) {
EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID);
if (m_World->ValidEntity(localEntityID)) {
return true;
}
deleteFromServerClientMaps(serverEntityID, localEntityID);
}
return false;
}
void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID clientEntityID)
@@ -400,3 +423,9 @@ void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID client
m_ClientIDToServerID.insert(std::make_pair(clientEntityID, serverEntityID));
}
void Client::deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID)
{
m_ServerIDToClientID.erase(serverEntityID);
m_ClientIDToServerID.erase(clientEntityID);
}
+64 -25
View File
@@ -21,6 +21,8 @@ void Server::Start(World* world, 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);
for (size_t i = 0; i < m_MaxConnections; i++) {
m_PlayerDefinitions[i].StopTime = std::clock();
}
@@ -175,35 +177,51 @@ void Server::broadcast(Packet& packet)
// Send snapshot fields
void Server::sendSnapshot()
{
// Should time this
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
for (auto& it : worldComponentPools) {
Packet packet(MessageType::Snapshot);
ComponentPool* componentPool = it.second;
ComponentInfo componentInfo = componentPool->ComponentInfo();
Packet packet(MessageType::Snapshot);
addChildrenToPacket(packet, EntityID_Invalid);
broadcast(packet);
}
// Component Type
packet.WriteString(componentInfo.Name);
for (auto& componentWrapper : *componentPool) {
// HACK: Send entity name
packet.WriteString(m_World->GetName(componentWrapper.EntityID));
// Components EntityID
packet.WritePrimitive(componentWrapper.EntityID);
// Parents EntityID
packet.WritePrimitive(m_World->GetParent(componentWrapper.EntityID));
for (auto& componentField : componentWrapper.Info.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(componentField);
if (fieldInfo.Type == "string") {
std::string& value = componentWrapper[componentField];
packet.WriteString(value);
} else {
packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride);
void Server::addChildrenToPacket(Packet & packet, EntityID entityID)
{
auto itPair = m_World->GetChildren(entityID);
std::unordered_map<std::string, ComponentPool*> worldComponentPools = m_World->GetComponentPools();
// Loop through every child
for (auto it = itPair.first; it != itPair.second; it++) {
EntityID childEntityID = it->second;
// Write EntityID and parentsID and Entity name
packet.WritePrimitive(childEntityID);
packet.WritePrimitive(entityID);
packet.WriteString(m_World->GetName(childEntityID));
// Write components to child
int numberOfComponents = 0;
for (auto& i : worldComponentPools) {
if (i.second->KnowsEntity(childEntityID)) {
numberOfComponents++;
}
}
// Write how many components should be read
packet.WritePrimitive(numberOfComponents);
for (auto& i : worldComponentPools) {
// If the entity exist in the pool
if (i.second->KnowsEntity(childEntityID)) {
ComponentWrapper componentWrapper = i.second->GetByEntity(childEntityID);
// ComponentType
packet.WriteString(componentWrapper.Info.Name);
// Loop through fields
for (auto& componentField : componentWrapper.Info.FieldsInOrder) {
ComponentInfo::Field_t fieldInfo = componentWrapper.Info.Fields.at(componentField);
if (fieldInfo.Type == "string") {
std::string& value = componentWrapper[componentField];
packet.WriteString(value);
} else {
packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride);
}
}
}
}
if (packet.Size() > packet.HeaderSize() + componentInfo.Name.size()) {
broadcast(packet);
}
// Go to to your children
addChildrenToPacket(packet, childEntityID);
}
}
@@ -449,3 +467,24 @@ bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e)
send(e.PlayerID, packet);
return false;
}
bool Server::OnEntityDeleted(const Events::EntityDeleted & e)
{
if (!e.Cascaded) {
Packet packet = Packet(MessageType::EntityDeleted);
packet.WritePrimitive<EntityID>(e.DeletedEntity);
broadcast(packet);
}
return false;
}
bool Server::OnComponentDeleted(const Events::ComponentDeleted & e)
{
if (!e.Cascaded) {
Packet packet = Packet(MessageType::ComponentDeleted);
packet.WritePrimitive<EntityID>(e.Entity);
packet.WriteString(e.ComponentType);
broadcast(packet);
}
return false;
}
+2 -2
View File
@@ -57,7 +57,7 @@ Game::Game(int argc, char* argv[])
m_FrameStack->Height = m_Renderer->Resolution().Height;
// Create a world
m_World = new World();
m_World = new World(m_EventBroker);
std::string mapToLoad = m_Config->Get<std::string>("Debug.LoadMap", "");
if (!mapToLoad.empty()) {
auto file = ResourceManager::Load<EntityFile>(mapToLoad);
@@ -77,7 +77,7 @@ Game::Game(int argc, char* argv[])
// All systems with orderlevel 0 will be updated first.
unsigned int updateOrderLevel = 0;
m_SystemPipeline->AddSystem<RaptorCopterSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<HealthSystem>(updateOrderLevel);
//m_SystemPipeline->AddSystem<HealthSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<PlayerMovementSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<InterpolationSystem>(updateOrderLevel);
m_SystemPipeline->AddSystem<SpawnerSystem>(updateOrderLevel);
-1
View File
@@ -12,7 +12,6 @@ HealthSystem::HealthSystem(World* m_World, EventBroker* eventBroker)
void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt)
{
//if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity)
ComponentWrapper player = m_World->GetComponent(component.EntityID, "Player");
double maxHealth = (double)component["MaxHealth"];
//process the DeltaHealthVector and change the entitys health accordingly
+1
View File
@@ -46,6 +46,7 @@ bool WeaponSystem::OnInputCommand(const Events::InputCommand& e)
bool WeaponSystem::OnShoot(const Events::Shoot& eShoot) {
// Screen center, based on current resolution!
//TODO: check if player has enough ammo and if weapon has a cooldown or not
Rectangle screenResolution = m_Renderer->Resolution();
glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2);