diff --git a/assets b/assets index 75778193..e4dc9529 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 757781933738bc4158c5c26750594b70acd537cd +Subproject commit e4dc9529f2178d373808ddf487165fa50a641a78 diff --git a/include/Engine/Core/EComponentDeleted.h b/include/Engine/Core/EComponentDeleted.h new file mode 100644 index 00000000..6d9c468d --- /dev/null +++ b/include/Engine/Core/EComponentDeleted.h @@ -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 \ No newline at end of file diff --git a/include/Engine/Core/EEntityDeleted.h b/include/Engine/Core/EEntityDeleted.h new file mode 100644 index 00000000..80e20e17 --- /dev/null +++ b/include/Engine/Core/EEntityDeleted.h @@ -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 \ No newline at end of file diff --git a/include/Engine/Core/EPlayerDamage.h b/include/Engine/Core/EPlayerDamage.h index e0f2acd7..a7e135ce 100644 --- a/include/Engine/Core/EPlayerDamage.h +++ b/include/Engine/Core/EPlayerDamage.h @@ -2,15 +2,15 @@ #define EPlayerDamage_h__ #include "EventBroker.h" -#include "../Core/Entity.h" +#include "../Core/EntityWrapper.h" namespace Events { struct PlayerDamage : Event { - double DamageAmount; - EntityID PlayerDamagedID; + EntityWrapper Player; + double Damage; }; } diff --git a/include/Engine/Core/EPlayerSpawned.h b/include/Engine/Core/EPlayerSpawned.h index a5700ed3..fcfaef61 100644 --- a/include/Engine/Core/EPlayerSpawned.h +++ b/include/Engine/Core/EPlayerSpawned.h @@ -12,6 +12,7 @@ struct PlayerSpawned : Event int PlayerID; EntityWrapper Player; EntityWrapper Spawner; + std::string PlayerName; }; } diff --git a/include/Engine/Core/EShoot.h b/include/Engine/Core/EShoot.h index fd54122f..76821a24 100644 --- a/include/Engine/Core/EShoot.h +++ b/include/Engine/Core/EShoot.h @@ -2,16 +2,14 @@ #define EShoot_h__ #include "EventBroker.h" -#include "../Core/Entity.h" -#include "Engine/GLM.h" +#include "../Core/EntityWrapper.h" namespace Events { struct Shoot : Event { - //ID for who made the shot - EntityID shooter; + EntityWrapper Player; }; } diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h index 711f5045..b2e0ce4c 100644 --- a/include/Engine/Core/EntityWrapper.h +++ b/include/Engine/Core/EntityWrapper.h @@ -23,9 +23,11 @@ struct EntityWrapper static const EntityWrapper Invalid; - bool HasComponent(const std::string& componentName); + const std::string Name(); + bool HasComponent(const std::string& componentType); EntityWrapper Parent(); EntityWrapper FirstChildByName(const std::string& name); + EntityWrapper FirstParentWithComponent(const std::string& componentType); bool IsChildOf(EntityWrapper potentialParent); bool Valid(); @@ -34,6 +36,9 @@ struct EntityWrapper bool operator!=(const EntityWrapper& e) const; explicit operator EntityID() const; operator bool(); + +private: + EntityWrapper firstChildByNameRecursive(const std::string& name, EntityID parent); }; namespace std diff --git a/include/Engine/Core/Transform.h b/include/Engine/Core/Transform.h index 3b0811c9..2f549140 100644 --- a/include/Engine/Core/Transform.h +++ b/include/Engine/Core/Transform.h @@ -8,8 +8,10 @@ namespace Transform { +glm::mat4 AbsoluteTransformation(EntityWrapper entity); glm::vec3 AbsolutePosition(EntityWrapper entity); glm::vec3 AbsolutePosition(World* world, EntityID entity); +glm::vec3 AbsoluteOrientationEuler(EntityWrapper entity); glm::quat AbsoluteOrientation(EntityWrapper entity); glm::quat AbsoluteOrientation(World* world, EntityID entity); glm::vec3 AbsoluteScale(EntityWrapper entity); diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index b201d4ac..35394b9f 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -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 m_EntityParents; @@ -55,6 +60,7 @@ private: std::unordered_map m_EntityNames; EntityID generateEntityID(); + void deleteEntityRecursive(EntityID entity, bool cascaded = false); }; #endif \ No newline at end of file diff --git a/include/Engine/Input/EInputCommand.h b/include/Engine/Input/EInputCommand.h index 1e150786..d4608b55 100644 --- a/include/Engine/Input/EInputCommand.h +++ b/include/Engine/Input/EInputCommand.h @@ -2,6 +2,7 @@ #define Events_InputCommand_h__ #include "Core/EventBroker.h" +#include "Core/EntityWrapper.h" namespace Events { @@ -10,6 +11,7 @@ struct InputCommand : Event { /** Numerical ID of the player. */ int PlayerID; + EntityWrapper Player; /** The command that was sent. */ std::string Command; /** The value of the command. */ diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index 92d78f4a..4f1baa67 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -50,6 +50,7 @@ private: PlayerID m_PlayerID = -1; EntityID m_ServerEntityID = std::numeric_limits::max(); bool m_IsConnected = false; + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; // Server Client Lookup map // Assumes that root node for client and server is EntityID 0. @@ -79,12 +80,15 @@ 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(); bool hasServerTimedOut(); EntityID createPlayer(); void sendInputCommands(); + void sendLocalPlayerTransform(); void becomePlayer(); // Mapping Logic // Returns if local EntityID exist in map @@ -92,13 +96,16 @@ 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; EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); - EventRelay m_EPlayeDamage; + EventRelay m_EPlayerDamage; bool OnPlayerDamage(const Events::PlayerDamage& e); + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(const Events::PlayerSpawned& e); }; #endif diff --git a/include/Engine/Network/MessageType.h b/include/Engine/Network/MessageType.h index 13ac5e9d..85f22649 100644 --- a/include/Engine/Network/MessageType.h +++ b/include/Engine/Network/MessageType.h @@ -15,7 +15,10 @@ enum class MessageType PlayerConnected, BecomePlayer, Kick, - OnPlayerSpawned + OnPlayerSpawned, + EntityDeleted, + ComponentDeleted, + PlayerTransform }; #endif diff --git a/include/Engine/Network/Network.h b/include/Engine/Network/Network.h index 480ac602..e1e64fc1 100644 --- a/include/Engine/Network/Network.h +++ b/include/Engine/Network/Network.h @@ -12,10 +12,9 @@ #include #include -#define INPUTSIZE 4097 +#define INPUTSIZE 32000 typedef unsigned int PlayerID; typedef unsigned int PacketID; -typedef unsigned int UserID; class Network { diff --git a/include/Engine/Network/PlayerDefinition.h b/include/Engine/Network/PlayerDefinition.h index 4b8b8e6e..863948b3 100644 --- a/include/Engine/Network/PlayerDefinition.h +++ b/include/Engine/Network/PlayerDefinition.h @@ -1,9 +1,10 @@ #ifndef PlayerDefinition_h__ #define PlayerDefinition_h__ #include +#include "../Core/Entity.h" struct PlayerDefinition { - int EntityID = -1; + ::EntityID EntityID = EntityID_Invalid; std::string Name = ""; boost::asio::ip::udp::endpoint Endpoint; unsigned int PacketID; diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index e7c745e7..11f983a9 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -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 { @@ -31,8 +33,8 @@ private: boost::asio::ip::udp::socket m_Socket; // Sending messages to client logic - PlayerDefinition m_PlayerDefinitions[8]; // - std::vector m_ConnectedUsers; + std::map m_ConnectedPlayers; + // HACK: Fix INPUTSIZE char readBuffer[INPUTSIZE] = { 0 }; int bytesRead = 0; // time for previouse message @@ -43,6 +45,7 @@ private: int pingIntervalMs; int snapshotInterval; int checkTimeOutInterval = 100; + int m_NextPlayerID = 0; //Timers std::clock_t m_StartPingTime; @@ -58,14 +61,14 @@ private: // Private member functions int receive(char* data); void readFromClients(); - void send(Packet& packet, UserID user); void send(PlayerID player, Packet& packet); void send(Packet& packet); void broadcast(Packet& packet); void sendSnapshot(); + void addChildrenToPacket(Packet& packet, EntityID entityID); void sendPing(); void checkForTimeOuts(); - void disconnect(UserID user); + void disconnect(PlayerID playerID); void parseMessageType(Packet& packet); void parseOnInputCommand(Packet& packet); void parseOnPlayerDamage(Packet& packet); @@ -74,7 +77,6 @@ private: void parseClientPing(); void parsePing(); void identifyPacketLoss(); - void createPlayer(); void kick(PlayerID player); PlayerID GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint); // Debug event @@ -82,6 +84,11 @@ private: bool OnInputCommand(const Events::InputCommand& e); EventRelay m_EPlayerSpawned; bool OnPlayerSpawned(const Events::PlayerSpawned& e); + EventRelay m_EEntityDeleted; + bool OnEntityDeleted(const Events::EntityDeleted& e); + EventRelay m_EComponentDeleted; + bool OnComponentDeleted(const Events::ComponentDeleted& e); + void parsePlayerTransform(Packet& packet); }; #endif diff --git a/include/Engine/Rendering/AnimationSystem.h b/include/Engine/Rendering/AnimationSystem.h new file mode 100644 index 00000000..fcdcbc92 --- /dev/null +++ b/include/Engine/Rendering/AnimationSystem.h @@ -0,0 +1,29 @@ +#ifndef AnimationSystem_h__ +#define AnimationSystem_h__ + +#include "GLM.h" + +#include "Common.h" +#include "Core/System.h" +#include "Core/ResourceManager.h" +#include "Rendering/Model.h" +#include "Rendering/EAnimationComplete.h" +#include "Rendering/Skeleton.h" + +class AnimationSystem : public PureSystem +{ +public: + AnimationSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Animation") + { + + } + ~AnimationSystem() { } + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& animationComponent, double dt) override; +private: + + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/EAnimationComplete.h b/include/Engine/Rendering/EAnimationComplete.h new file mode 100644 index 00000000..00519a38 --- /dev/null +++ b/include/Engine/Rendering/EAnimationComplete.h @@ -0,0 +1,18 @@ +#ifndef Events_AnimationComplete_h__ +#define Events_AnimationComplete_h__ + +#include "../Core/EventBroker.h" +#include "../Core/EntityWrapper.h" + +namespace Events +{ + +struct AnimationComplete : Event +{ + EntityWrapper Entity; + std::string Name; +}; + +} + +#endif diff --git a/include/Engine/Rendering/ModelJob.h b/include/Engine/Rendering/ModelJob.h index 01f4cca4..df7f1aec 100644 --- a/include/Engine/Rendering/ModelJob.h +++ b/include/Engine/Rendering/ModelJob.h @@ -13,10 +13,11 @@ #include "Camera.h" #include "../Core/World.h" #include "../Core/Transform.h" +#include "Skeleton.h" struct ModelJob : RenderJob { - ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world) + ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world, glm::vec4 fillColor, float fillPercentage) : RenderJob() { Model = model; @@ -37,6 +38,17 @@ struct ModelJob : RenderJob glm::vec3 worldpos = glm::vec3(camera->ViewMatrix() * glm::vec4(abspos, 1)); Depth = worldpos.z; World = world; + + + FillColor = fillColor; + FillPercentage = fillPercentage; + Skeleton = Model->m_RawModel->m_Skeleton; + + if (world->HasComponent(Entity, "Animation") && Skeleton != nullptr) { + auto animationComponent = world->GetComponent(Entity, "Animation"); + Animation = model->m_RawModel->m_Skeleton->GetAnimation(animationComponent["Name"]); + AnimationTime = (double)animationComponent["Time"]; + } }; unsigned int TextureID; @@ -51,6 +63,11 @@ struct ModelJob : RenderJob float Shininess = 0.f; glm::vec4 Color; const ::Model* Model = nullptr; + ::Skeleton* Skeleton = nullptr; + const ::Skeleton::Animation* Animation = nullptr; + + float AnimationTime = 0.f; + glm::vec4 DiffuseColor; glm::vec4 SpecularColor; glm::vec4 IncandescenceColor; @@ -58,6 +75,9 @@ struct ModelJob : RenderJob unsigned int EndIndex = 0; World* World; + glm::vec4 FillColor = glm::vec4(0); + float FillPercentage = 0.0; + void CalculateHash() override { Hash = TextureID; diff --git a/include/Engine/Rendering/PickingPass.h b/include/Engine/Rendering/PickingPass.h index 5fc88982..da3ed31b 100644 --- a/include/Engine/Rendering/PickingPass.h +++ b/include/Engine/Rendering/PickingPass.h @@ -1,8 +1,6 @@ #ifndef PickingPass_h__ #define PickingPass_h__ - - #include "IRenderer.h" #include "PickingPassState.h" #include "FrameBuffer.h" @@ -10,8 +8,7 @@ #include "Util/UnorderedMapiVec2.h" #include "../Core/EventBroker.h" #include "../Core/World.h" - - +#include "Rendering/Skeleton.h" class PickingPass { diff --git a/include/Engine/Rendering/RenderSystem.h b/include/Engine/Rendering/RenderSystem.h index 005d2eb7..37f99341 100644 --- a/include/Engine/Rendering/RenderSystem.h +++ b/include/Engine/Rendering/RenderSystem.h @@ -29,6 +29,7 @@ private: const IRenderer* m_Renderer; RenderFrame* m_RenderFrame; Camera* m_Camera; + World* m_World; EntityWrapper m_CurrentCamera = EntityWrapper::Invalid; EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index f9843f11..3b069349 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -24,7 +24,7 @@ public: private: //methods which will take care of specific events EventRelay m_EPlayerDamage; - bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e); + bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e); diff --git a/include/Game/Systems/LifetimeSystem.h b/include/Game/Systems/LifetimeSystem.h new file mode 100644 index 00000000..da88cfa2 --- /dev/null +++ b/include/Game/Systems/LifetimeSystem.h @@ -0,0 +1,21 @@ +#ifndef LifetimeSystem_h__ +#define LifetimeSystem_h__ + +#include "Core/System.h" + +class LifetimeSystem : public ImpureSystem, PureSystem +{ +public: + LifetimeSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Lifetime") + { } + + virtual void Update(double dt) override; + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cLifetime, double dt) override; + +private: + std::vector m_Deletions; +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PlayerHUD.h b/include/Game/Systems/PlayerHUD.h new file mode 100644 index 00000000..180f5a2a --- /dev/null +++ b/include/Game/Systems/PlayerHUD.h @@ -0,0 +1,23 @@ +#ifndef PlayerHUD_h__ +#define PlayerHUD_h__ + +#include "../../Engine/Core/System.h" +#include "../../Engine/GLM.h" +#include "../../Engine/Rendering/ESetCamera.h" +#include + +class PlayerHUD : public ImpureSystem +{ +public: + PlayerHUD(World* world, EventBroker* eventBrokerer); + ~PlayerHUD(); + + virtual void Update(double dt) override; + +private: + World* m_World; + EventBroker* m_EventBroker; + +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/WeaponSystem.h b/include/Game/Systems/WeaponSystem.h index 5acf72b3..0dd5cc54 100644 --- a/include/Game/Systems/WeaponSystem.h +++ b/include/Game/Systems/WeaponSystem.h @@ -9,7 +9,10 @@ #include "Core/System.h" #include "Core/EPlayerDamage.h" #include "Core/EShoot.h" +#include "Core/EPlayerSpawned.h" #include "Input/EInputCommand.h" +#include "Core/EntityFile.h" +#include "Core/EntityFileParser.h" #include #include @@ -23,16 +26,18 @@ public: virtual void Update(double dt) override; private: - //methods which will take care of specific events - EventRelay m_EShoot; - bool WeaponSystem::OnShoot(const Events::Shoot& e); - - EventRelay m_EInputCommand; - bool WeaponSystem::OnInputCommand(const Events::InputCommand& e); - IRenderer* m_Renderer; - std::vector> m_EShootVector; + // State + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; + + // Events + EventRelay m_EPlayerSpawned; + bool WeaponSystem::OnPlayerSpawned(const Events::PlayerSpawned& e); + EventRelay m_EShoot; + bool WeaponSystem::OnShoot(Events::Shoot& e); + EventRelay m_EInputCommand; + bool WeaponSystem::OnInputCommand(const Events::InputCommand& e); }; #endif \ No newline at end of file diff --git a/resources/DefaultInput.ini b/resources/DefaultInput.ini index 5ed46241..d07ed6a3 100644 --- a/resources/DefaultInput.ini +++ b/resources/DefaultInput.ini @@ -21,4 +21,5 @@ F1=ToggleEditor X=EditorToggleTransformSpace C=ConnectToServer N=SwitchToServer -M=SwitchToClient \ No newline at end of file +M=SwitchToClient +P=SwitchToPlayer \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 12d94611..5ce7fa5f 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -23,4 +23,8 @@ + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Animation.xml b/resources/Schema/Components/Animation.xml new file mode 100644 index 00000000..01af2747 --- /dev/null +++ b/resources/Schema/Components/Animation.xml @@ -0,0 +1,6 @@ + + + + 0 + true + \ No newline at end of file diff --git a/resources/Schema/Components/Animation.xsd b/resources/Schema/Components/Animation.xsd new file mode 100644 index 00000000..0dd21f29 --- /dev/null +++ b/resources/Schema/Components/Animation.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Fill.xml b/resources/Schema/Components/Fill.xml new file mode 100644 index 00000000..397f8e49 --- /dev/null +++ b/resources/Schema/Components/Fill.xml @@ -0,0 +1,5 @@ + + + 0.0 + + \ No newline at end of file diff --git a/resources/Schema/Components/Fill.xsd b/resources/Schema/Components/Fill.xsd new file mode 100644 index 00000000..2bf6548f --- /dev/null +++ b/resources/Schema/Components/Fill.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/HealthHUD.xml b/resources/Schema/Components/HealthHUD.xml new file mode 100644 index 00000000..d7e20f92 --- /dev/null +++ b/resources/Schema/Components/HealthHUD.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/HealthHUD.xsd b/resources/Schema/Components/HealthHUD.xsd new file mode 100644 index 00000000..821a65a2 --- /dev/null +++ b/resources/Schema/Components/HealthHUD.xsd @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Lifetime.xml b/resources/Schema/Components/Lifetime.xml new file mode 100644 index 00000000..d7e230d7 --- /dev/null +++ b/resources/Schema/Components/Lifetime.xml @@ -0,0 +1,4 @@ + + + 0 + \ No newline at end of file diff --git a/resources/Schema/Components/Lifetime.xsd b/resources/Schema/Components/Lifetime.xsd new file mode 100644 index 00000000..137c1d4a --- /dev/null +++ b/resources/Schema/Components/Lifetime.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Entities/CollidableCube.xml b/resources/Schema/Entities/CollidableCube.xml index ebba54be..fd800a8b 100644 --- a/resources/Schema/Entities/CollidableCube.xml +++ b/resources/Schema/Entities/CollidableCube.xml @@ -5,7 +5,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/Empty.xml b/resources/Schema/Entities/Empty.xml index c9b8924f..6efd8318 100644 --- a/resources/Schema/Entities/Empty.xml +++ b/resources/Schema/Entities/Empty.xml @@ -5,16 +5,6 @@ - - - - - - - - - - - + diff --git a/resources/Schema/Entities/FastWorld.xml b/resources/Schema/Entities/FastWorld.xml new file mode 100644 index 00000000..68d1dccb --- /dev/null +++ b/resources/Schema/Entities/FastWorld.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + + Crouch Walk + + 32 + + + Models/AssaultAnimated.mesh + + + + + + + + + + + pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml new file mode 100644 index 00000000..97fd3f4d --- /dev/null +++ b/resources/Schema/Entities/GameMap.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + Models\MapVersion1.mesh + + + + + + + + + 2 + + + Models/DirectionalLightWidget.mesh + false + + + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 40de747a..1378f623 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -146,7 +146,6 @@ - @@ -178,6 +177,123 @@ + + + + + + + + + + false + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + false + + + + + + + + + + + + + Models/Camera.mesh + + + + + + + + + + + + + + + Models/Camera.mesh + false + + + + + + + + + + + + Models/AssaultHeadless.mesh + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 4316091b..7914f82b 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -2,14 +2,14 @@ + - - + 5 @@ -20,8 +20,7 @@ - - + @@ -31,7 +30,6 @@ - @@ -40,10 +38,9 @@ Fonts/DroidSans.ttf,100 - false - + @@ -54,6 +51,7 @@ Models/Camera.mesh + false @@ -62,6 +60,67 @@ + + + + + + + + + + + 1 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Models/CrosshairQuad.mesh + + + + + + + + + + + + + Models/AssaultWeapon.mesh + + + + + + + + + + + + + + + + + + @@ -80,12 +139,16 @@ + + Hold Pos + + 1 + - Models/AssaultHeadless.mesh + Models/AssaultAnimated.mesh + - - - + diff --git a/resources/Schema/Entities/PointLight.xml b/resources/Schema/Entities/PointLight.xml new file mode 100644 index 00000000..64c02753 --- /dev/null +++ b/resources/Schema/Entities/PointLight.xml @@ -0,0 +1,16 @@ + + + + + + 8 + 2.7999999523162842 + + + + + + + + + diff --git a/resources/Schema/Entities/RayBlue b/resources/Schema/Entities/RayBlue new file mode 100644 index 00000000..d34e185e --- /dev/null +++ b/resources/Schema/Entities/RayBlue @@ -0,0 +1,17 @@ + + + + + + Models/Core/UnitCylinder.mesh + + + + + + + + + + + diff --git a/resources/Schema/Entities/RayBlue.xml b/resources/Schema/Entities/RayBlue.xml new file mode 100644 index 00000000..3b985a7e --- /dev/null +++ b/resources/Schema/Entities/RayBlue.xml @@ -0,0 +1,19 @@ + + + + + + 0.25 + + + Models/CylinderBullet.mesh + + + + + + + + + + diff --git a/resources/Schema/Entities/RayRed b/resources/Schema/Entities/RayRed new file mode 100644 index 00000000..d34e185e --- /dev/null +++ b/resources/Schema/Entities/RayRed @@ -0,0 +1,17 @@ + + + + + + Models/Core/UnitCylinder.mesh + + + + + + + + + + + diff --git a/resources/Schema/Entities/RayRed.xml b/resources/Schema/Entities/RayRed.xml new file mode 100644 index 00000000..df563476 --- /dev/null +++ b/resources/Schema/Entities/RayRed.xml @@ -0,0 +1,19 @@ + + + + + + 0.25 + + + Models/CylinderBullet.mesh + + + + + + + + + + diff --git a/resources/Schema/Entities/RenderingWorld.xml b/resources/Schema/Entities/RenderingWorld.xml index 796a66f0..20301d5f 100644 --- a/resources/Schema/Entities/RenderingWorld.xml +++ b/resources/Schema/Entities/RenderingWorld.xml @@ -6,34 +6,115 @@ - + - - ActionCamera - - - Models/Camera.mesh - + + 65.990005493164063 + + - - + - + - - Camera #2 - Fonts/DroidSans.ttf,64 - 0 - + + Models/Assault.obj + + - - + + + + + 80 + + + Models/Camera.mesh + + + + + + + + + + + + + + + + + + + 0.65990006923675537 + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + 24 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + 262 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + + + + + + 65/100 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + @@ -42,7 +123,7 @@ Models/Core/UnitPlane.mesh - + @@ -52,6 +133,7 @@ Models/Assault.mesh + @@ -64,6 +146,7 @@ Welcome! Fonts/DroidSans.ttf,1280 + @@ -74,32 +157,40 @@ - 2 - + 3 + - - + + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -111,21 +202,30 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -137,21 +237,30 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - - 8 + + 70 @@ -163,19 +272,28 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -191,6 +309,7 @@ Models/Assault.mesh + @@ -198,46 +317,15 @@ - - - - MainCamera - - - - Models/Camera.mesh - false - - - - - - - - - - - Taiwan #1 - Fonts/DroidSans.ttf,64 - 0 - - - - - - - - - - - + - 0.80000001192092896 + 0.10000000149011612 Models/DirectionalLightWidget.mesh + @@ -250,6 +338,7 @@ Models/Core/UnitCube.mesh + diff --git a/resources/Schema/Entities/Spawnpoint b/resources/Schema/Entities/Spawnpoint new file mode 100644 index 00000000..a53aaa08 --- /dev/null +++ b/resources/Schema/Entities/Spawnpoint @@ -0,0 +1,32 @@ + + + + + + + Models/Core/UnitCube.mesh + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 5583b8b8..5d135847 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -32,6 +32,7 @@ + diff --git a/resources/Shaders/ForwardPlus.frag.glsl b/resources/Shaders/ForwardPlus.frag.glsl index 57b1d627..f33d870b 100644 --- a/resources/Shaders/ForwardPlus.frag.glsl +++ b/resources/Shaders/ForwardPlus.frag.glsl @@ -6,9 +6,12 @@ uniform mat4 P; uniform vec4 Color; uniform vec4 DiffuseColor; uniform vec2 ScreenDimensions; +uniform vec4 FillColor; +uniform float FillPercentage; layout (binding = 0) uniform sampler2D DiffuseTexture; layout (binding = 1) uniform sampler2D GlowMap; + #define TILE_SIZE 16 struct LightSource { @@ -136,10 +139,21 @@ void main() //sceneColor += Input.DiffuseColor; vec4 color_result = DiffuseColor * (totalLighting.Diffuse + totalLighting.Specular) * diffuseTexel * Color; + + + float pos = ((P * vec4(Input.Position, 1)).y + 1.0)/2.0; + + if(pos <= FillPercentage) { + color_result += FillColor; + } //bloomColor = vec4(0.3, 0.8, 0.6, 1.0); sceneColor = vec4(color_result.xyz, clamp(color_result.a, 0, 1)); //These if statements should be removed if they are slow. color_result += glowTexel; + + + + bloomColor = vec4(clamp(color_result.xyz - 1.0, 0, 100), 1.0); /* if(color_result.x > 1 || color_result.y > 1 || color_result.z > 1) { @@ -150,8 +164,9 @@ void main() //sceneColor += Input.DiffuseColor * (totalLighting.Diffuse) * diffuseTexel * Color; //sceneColor += Input.DiffuseColor + vec4(0.0, LightGrids.Data[currentTile].Amount/3, 0, 1); - //sceneColor = diffuseTexel * Input.DiffuseColor * Color; - //sceneColor += vec4(currentTile/3600.f, 0, 0, 1); + + + //Tiled Debug Code /* diff --git a/resources/Shaders/Picking.vert.glsl b/resources/Shaders/Picking.vert.glsl index b2857cea..205a8fdd 100644 --- a/resources/Shaders/Picking.vert.glsl +++ b/resources/Shaders/Picking.vert.glsl @@ -3,18 +3,15 @@ uniform mat4 M; uniform mat4 V; uniform mat4 P; +uniform mat4 Bones[100]; layout(location = 0) in vec3 Position; layout(location = 1) in vec3 Normal; layout(location = 2) in vec3 Tangent; layout(location = 3) in vec3 BiTangent; layout(location = 4) in vec2 TextureCoords; -layout(location = 5) in vec4 DiffuseVertexColor; -layout(location = 6) in vec4 SpecularVertexColor; -layout(location = 7) in vec4 BoneIndices1; -layout(location = 8) in vec4 BoneIndices2; -layout(location = 9) in vec4 BoneWeights1; -layout(location = 10) in vec4 BoneWeights2; +layout(location = 5) in vec4 BoneIndices; +layout(location = 6) in vec4 BoneWeights; out VertexData{ vec3 Position; @@ -22,7 +19,14 @@ out VertexData{ void main() { - gl_Position = P * V* M * vec4(Position, 1.0); + mat4 boneTransform = mat4(1); + if(BoneWeights[0] > 0.0f){ + boneTransform = BoneWeights[0] * Bones[int(BoneIndices[0])] + + BoneWeights[1] * Bones[int(BoneIndices[1])] + + BoneWeights[2] * Bones[int(BoneIndices[2])] + + BoneWeights[3] * Bones[int(BoneIndices[3])]; + } - Output.Position = Position; + gl_Position = P*V*M*boneTransform * vec4(Position, 1.0); + Output.Position = (boneTransform * vec4(Position, 1.0)).xyz; } \ No newline at end of file diff --git a/src/Engine/Core/EntityWrapper.cpp b/src/Engine/Core/EntityWrapper.cpp index 55d341e1..1f3fc1ab 100644 --- a/src/Engine/Core/EntityWrapper.cpp +++ b/src/Engine/Core/EntityWrapper.cpp @@ -3,6 +3,11 @@ const EntityWrapper EntityWrapper::Invalid = EntityWrapper(nullptr, EntityID_Invalid); +const std::string EntityWrapper::Name() +{ + return World->GetName(ID); +} + bool EntityWrapper::HasComponent(const std::string& componentName) { if (!Valid()) { @@ -22,17 +27,18 @@ EntityWrapper EntityWrapper::Parent() EntityWrapper EntityWrapper::FirstChildByName(const std::string& name) { - auto itPair = this->World->GetChildren(this->ID); - if (itPair.first == itPair.second) { - return EntityWrapper::Invalid; - } + return firstChildByNameRecursive(name, this->ID); +} - for (auto it = itPair.first; it != itPair.second; ++it) { - if (this->World->GetName(it->second) == name) { - return EntityWrapper(this->World, it->second); +EntityWrapper EntityWrapper::FirstParentWithComponent(const std::string& componentType) +{ + EntityWrapper entity = *this; + while (entity.Parent().Valid()) { + entity = entity.Parent(); + if (entity.HasComponent(componentType)) { + return entity; } } - return EntityWrapper::Invalid; } @@ -96,3 +102,29 @@ EntityWrapper::operator bool() return this->Valid(); } +EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name, EntityID parent) +{ + if (!this->World->ValidEntity(parent)) { + return EntityWrapper::Invalid; + } + + auto itPair = this->World->GetChildren(parent); + if (itPair.first == itPair.second) { + return EntityWrapper::Invalid; + } + + for (auto it = itPair.first; it != itPair.second; ++it) { + std::string itName = this->World->GetName(it->second); + if (itName == name) { + return EntityWrapper(this->World, it->second); + } else if (it->second != EntityID_Invalid) { + EntityWrapper result = firstChildByNameRecursive(name, it->second); + if (result != EntityWrapper::Invalid) { + return result; + } + } + } + + return EntityWrapper::Invalid; +} + diff --git a/src/Engine/Core/Transform.cpp b/src/Engine/Core/Transform.cpp index c9869014..31b4493f 100644 --- a/src/Engine/Core/Transform.cpp +++ b/src/Engine/Core/Transform.cpp @@ -1,5 +1,17 @@ #include "Core/Transform.h" +glm::mat4 Transform::AbsoluteTransformation(EntityWrapper entity) +{ + glm::mat4 t = glm::mat4(1.f); + + while (entity.Valid()) { + t = glm::translate((glm::vec3&)entity["Transform"]["Position"]) * glm::toMat4(glm::quat((glm::vec3&)entity["Transform"]["Orientation"])) * glm::scale((glm::vec3&)entity["Transform"]["Scale"]) * t; + entity = entity.Parent(); + } + + return t; +} + glm::vec3 Transform::AbsolutePosition(EntityWrapper entity) { return AbsolutePosition(entity.World, entity.ID); @@ -19,6 +31,19 @@ glm::vec3 Transform::AbsolutePosition(World* world, EntityID entity) return position; } +glm::vec3 Transform::AbsoluteOrientationEuler(EntityWrapper entity) +{ + glm::vec3 orientation; + + while (entity.Valid()) { + ComponentWrapper transform = entity["Transform"]; + orientation += (glm::vec3)transform["Orientation"]; + entity = entity.Parent(); + } + + return orientation; +} + glm::quat Transform::AbsoluteOrientation(EntityWrapper entity) { return AbsoluteOrientation(entity.World, entity.ID); @@ -62,6 +87,8 @@ glm::mat4 Transform::ModelMatrix(EntityWrapper entity) glm::mat4 Transform::ModelMatrix(EntityID entity, World* world) { + return AbsoluteTransformation(EntityWrapper(world, entity)); + glm::vec3 position = Transform::AbsolutePosition(world, entity); glm::quat orientation = Transform::AbsoluteOrientation(world, entity); glm::vec3 scale = Transform::AbsoluteScale(world, entity); diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 477e2ab2..0c4c9ce4 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -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 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,57 @@ EntityID World::generateEntityID() return m_CurrentEntityID++; } +void World::deleteEntityRecursive(EntityID entity, bool cascaded /*= false*/) +{ + // Don't attempt to delete entities that don't exist anyway + if (!ValidEntity(entity)) { + return; + } + + 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 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); +} + diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index 390e69a5..1200ce34 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -1,4 +1,5 @@ #include "Editor/EditorGUI.h" +#include "Rendering/ESetCamera.h" EditorGUI::EditorGUI(World* world, EventBroker* eventBroker) : m_World(world) @@ -305,6 +306,14 @@ bool EditorGUI::drawComponentNode(EntityWrapper entity, const ComponentInfo& ci) } } + if (ci.Name == "Camera") { + if (ImGui::Button("Activate")) { + Events::SetCamera e; + e.CameraEntity = entity; + m_EventBroker->Publish(e); + } + } + return true; } diff --git a/src/Engine/Editor/EditorRenderSystem.cpp b/src/Engine/Editor/EditorRenderSystem.cpp index ed054052..2aad1340 100644 --- a/src/Engine/Editor/EditorRenderSystem.cpp +++ b/src/Engine/Editor/EditorRenderSystem.cpp @@ -48,7 +48,7 @@ void EditorRenderSystem::Update(double dt) EntityWrapper entity(m_World, cModel.EntityID); glm::mat4 modelMatrix = Transform::ModelMatrix(entity.ID, entity.World); for (auto matGroup : model->MaterialGroups()) { - std::shared_ptr modelJob = std::make_shared(model, scene.Camera, modelMatrix, matGroup, cModel, entity.World); + std::shared_ptr modelJob = std::make_shared(model, scene.Camera, modelMatrix, matGroup, cModel, entity.World, glm::vec4(0), 0.f); scene.ForwardJobs.push_back(modelJob); } } diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 9c0e2e47..6ffd0e24 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -203,7 +203,7 @@ bool EditorSystem::OnSetCamera(const Events::SetCamera& e) m_ActualCamera = e.CameraEntity; Events::SetCamera e2; e2.CameraEntity = m_EditorCamera; - m_EventBroker->Publish(e2); + //m_EventBroker->Publish(e2); } return true; } diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index 49964128..6c43cc8b 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -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("Networking.Address", "127.0.0.1"); - int port = config->Get("Networking.Port", 13); + int port = config->Get("Networking.Port", 27666); m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port); // Set up network stream m_PlayerName = config->Get("Networking.Name", "Raptorcopter"); @@ -31,7 +31,8 @@ void Client::Start(World* world, EventBroker* eventBroker) // Subscribe to events EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand); - EVENT_SUBSCRIBE_MEMBER(m_EPlayeDamage, &Client::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned); m_Socket.connect(m_ReceiverEndpoint); LOG_INFO("I am client. BIP BOP"); @@ -48,6 +49,7 @@ void Client::Update() sendInputCommands(); m_TimeSinceSentInputs = std::clock(); } + sendLocalPlayerTransform(); } Network::Update(); } @@ -96,6 +98,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,20 +136,43 @@ 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()]); e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); e.PlayerID = -1; + e.PlayerName = packet.ReadString(); m_EventBroker->Publish(e); } +void Client::parseEntityDeletion(Packet & packet) +{ + EntityID entityToDelete = packet.ReadPrimitive(); + // TODO: What if an entity that didn't previously exist comes as a delete request and later comes in a delayed snapshot? + if (m_ServerIDToClientID.find(entityToDelete) != m_ServerIDToClientID.end()) { + 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(); + 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 +207,53 @@ 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(); - // HACK - m_World->SetName(receivedEntityID, entityName); - // Parents EntityID - EntityID receivedParentEntityID = packet.ReadPrimitive(); - 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 serverParentID = packet.ReadPrimitive(); + std::string serverEntityName = packet.ReadString(); + int ammountOfComponents = packet.ReadPrimitive(); + 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 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); + } } 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::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,13 +344,40 @@ bool Client::OnInputCommand(const Events::InputCommand & e) bool Client::OnPlayerDamage(const Events::PlayerDamage & e) { - Packet packet(MessageType::OnInputCommand, m_SendPacketID); - packet.WritePrimitive(e.DamageAmount); - packet.WritePrimitive(e.PlayerDamagedID); + Packet packet(MessageType::OnPlayerDamage, m_SendPacketID); + packet.WritePrimitive(e.Damage); + packet.WritePrimitive(m_ClientIDToServerID.at(e.Player.ID)); send(packet); return false; } +bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e) +{ + if (e.PlayerID == -1) { + m_LocalPlayer = e.Player; + } + return true; +} + +void Client::sendLocalPlayerTransform() +{ + if (!m_LocalPlayer.Valid()) { + return; + } + + ComponentWrapper cTransform = m_LocalPlayer["Transform"]; + glm::vec3& position = cTransform["Position"]; + glm::vec3& orientation = cTransform["Orientation"]; + Packet packet(MessageType::PlayerTransform, m_SendPacketID); + packet.WritePrimitive(position.x); + packet.WritePrimitive(position.y); + packet.WritePrimitive(position.z); + packet.WritePrimitive(orientation.x); + packet.WritePrimitive(orientation.y); + packet.WritePrimitive(orientation.z); + send(packet); +} + void Client::identifyPacketLoss() { // if no packets lost, difference should be equal to 1 @@ -386,12 +431,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 +459,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); +} diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index 3ab3dd5f..adf810aa 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -21,9 +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); - for (size_t i = 0; i < m_MaxConnections; i++) { - m_PlayerDefinitions[i].StopTime = std::clock(); - } + EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted); + EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted); LOG_INFO("I am Server. BIP BOP\n"); } @@ -96,8 +95,8 @@ void Server::parseMessageType(Packet& packet) case MessageType::OnPlayerDamage: parseOnPlayerDamage(packet); break; - case MessageType::BecomePlayer: - createPlayer(); + case MessageType::PlayerTransform: + parsePlayerTransform(packet); break; default: break; @@ -119,31 +118,22 @@ int Server::receive(char * data) return length; } -void Server::send(Packet& packet, UserID user) -{ - int bytesSent = m_Socket.send_to( - boost::asio::buffer(packet.Data(), packet.Size()), - m_ConnectedUsers[user].Endpoint, - 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; - } -} - void Server::send(PlayerID player, Packet& packet) { - int bytesSent = m_Socket.send_to( - boost::asio::buffer(packet.Data(), packet.Size()), - m_PlayerDefinitions[player].Endpoint, - 0); - // Network Debug data - if (isReadingData) { - m_NetworkData.TotalDataSent += packet.Size(); - m_NetworkData.DataSentThisInterval += packet.Size(); - m_NetworkData.AmountOfMessagesSent++; + try { + int bytesSent = m_Socket.send_to( + boost::asio::buffer(packet.Data(), packet.Size()), + m_ConnectedPlayers[player].Endpoint, + 0); + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataSent += packet.Size(); + m_NetworkData.DataSentThisInterval += packet.Size(); + m_NetworkData.AmountOfMessagesSent++; + } + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + m_ConnectedPlayers[player].Endpoint = boost::asio::ip::udp::endpoint(); } } @@ -164,58 +154,72 @@ void Server::send(Packet & packet) void Server::broadcast(Packet& packet) { - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - packet.ChangePacketID(m_ConnectedUsers[i].PacketID); - send(packet, i); - } + for (auto& kv : m_ConnectedPlayers) { + packet.ChangePacketID(kv.second.PacketID); + send(kv.first, packet); } } // Send snapshot fields void Server::sendSnapshot() { - // Should time this - std::unordered_map 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 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); } } void Server::sendPing() { // Prints connected players ping - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - int ping = 1000 * (m_ConnectedUsers[i].StopTime - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); - LOG_INFO("Last packetID received %i: User %i's ping: %i", m_ConnectedUsers[i].PacketID, i, std::abs(ping)); - } - } + //for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + // if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { + // int ping = 1000 * (m_ConnectedPlayers[i].StopTime - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); + // LOG_INFO("Last packetID received %i: User %i's ping: %i", m_ConnectedPlayers[i].PacketID, i, std::abs(ping)); + // } + //} // Create ping message Packet packet(MessageType::Ping); packet.WriteString("Ping from server"); @@ -230,9 +234,9 @@ void Server::checkForTimeOuts() int startPing = 1000 * m_StartPingTime / static_cast(CLOCKS_PER_SEC); - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - int stopPing = 1000 * m_ConnectedUsers[i].StopTime / + for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { + int stopPing = 1000 * m_ConnectedPlayers[i].StopTime / static_cast(CLOCKS_PER_SEC); if (startPing > stopPing + m_TimeoutMs) { LOG_INFO("User %i timed out!", i); @@ -242,40 +246,30 @@ void Server::checkForTimeOuts() } } -void Server::disconnect(UserID user) +void Server::disconnect(PlayerID playerID) { //broadcast("A player disconnected"); - LOG_INFO("User %s disconnected/timed out", m_PlayerDefinitions[user].Name.c_str()); + LOG_INFO("User %s disconnected/timed out", m_ConnectedPlayers[playerID].Name.c_str()); // Remove enteties and stuff (When we can remove entity, remove it and tell clients to remove the copy they have) Events::PlayerDisconnected e; - e.Entity = m_PlayerDefinitions[user].EntityID; - e.PlayerID = user; + e.Entity = m_ConnectedPlayers[playerID].EntityID; + e.PlayerID = playerID; m_EventBroker->Publish(e); - m_PlayerDefinitions[user].Endpoint = boost::asio::ip::udp::endpoint(); - m_PlayerDefinitions[user].EntityID = -1; - m_PlayerDefinitions[user].Name = ""; - m_PlayerDefinitions[user].PacketID = 0; - m_ConnectedUsers.erase(m_ConnectedUsers.begin() + user); + m_ConnectedPlayers.erase(playerID); } void Server::parseOnInputCommand(Packet& packet) { PlayerID player = -1; // Check which player it was who sent the message - for (int i = 0; i < m_MaxConnections; i++) { - // if the player is connected set playerID to the correct PlayerID - if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address() - && m_PlayerDefinitions[i].Endpoint.port() == m_ReceiverEndpoint.port()) { - player = i; - break; - } - } + player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); if (player != -1) { while (packet.DataReadSize() < packet.Size()) { Events::InputCommand e; e.Command = packet.ReadString(); e.PlayerID = player; // Set correct player id + e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); e.Value = packet.ReadPrimitive(); m_EventBroker->Publish(e); //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); @@ -286,8 +280,8 @@ void Server::parseOnInputCommand(Packet& packet) void Server::parseOnPlayerDamage(Packet & packet) { Events::PlayerDamage e; - e.DamageAmount = packet.ReadPrimitive(); - e.PlayerDamagedID = packet.ReadPrimitive(); + e.Damage = packet.ReadPrimitive(); + e.Player = EntityWrapper(m_World, packet.ReadPrimitive()); m_EventBroker->Publish(e); //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } @@ -299,9 +293,9 @@ void Server::parseConnect(Packet& packet) if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { return; } - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address() && - m_ConnectedUsers[i].Endpoint.port() == m_ReceiverEndpoint.port()) { + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && + kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { // Already connected return; } @@ -313,11 +307,11 @@ void Server::parseConnect(Packet& packet) pd.Name = packet.ReadString(); pd.PacketID = 0; pd.StopTime = std::clock(); - m_ConnectedUsers.push_back(pd); + m_ConnectedPlayers[m_NextPlayerID++] = pd; LOG_INFO("Spectator \"%s\" connected on IP: %s", pd.Name.c_str(), pd.Endpoint.address().to_string().c_str()); // Send a message to the player that connected - Packet connnectPacket(MessageType::Connect, m_ConnectedUsers[m_ConnectedUsers.size() - 1].PacketID); + Packet connnectPacket(MessageType::Connect, pd.PacketID); send(connnectPacket); // Send notification that a player has connected @@ -329,9 +323,10 @@ void Server::parseDisconnect() { LOG_INFO("%i: Parsing disconnect", m_PacketID); - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - disconnect(i); + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && + kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { + disconnect(kv.first); break; } } @@ -345,16 +340,16 @@ void Server::parseClientPing() return; } // Return ping - Packet packet(MessageType::Ping, m_PlayerDefinitions[player].PacketID); + Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); packet.WriteString("Ping received"); send(packet); } void Server::parsePing() { - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - m_ConnectedUsers[i].StopTime = std::clock(); + for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + if (m_ConnectedPlayers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { + m_ConnectedPlayers[i].StopTime = std::clock(); break; } } @@ -369,43 +364,6 @@ void Server::identifyPacketLoss() } } -void Server::createPlayer() -{ - if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { - // Already connected as player - LOG_WARNING("Already connected!"); - return; - } - UserID userIndex; - for (userIndex = 0; userIndex < m_ConnectedUsers.size(); userIndex++) { - if (m_ConnectedUsers[userIndex].Endpoint.address() == m_ReceiverEndpoint.address() && - m_ConnectedUsers[userIndex].Endpoint.port() == m_ReceiverEndpoint.port()) { - // Found user - break; - } - } - if (userIndex == m_ConnectedUsers.size()) { - LOG_WARNING("Not a recognized user!"); - return; - } - for (PlayerID playerIndex = 0; playerIndex < m_MaxConnections; playerIndex++) { - if (m_PlayerDefinitions[playerIndex].Endpoint.address() == boost::asio::ip::address()) { - m_PlayerDefinitions[playerIndex] = m_ConnectedUsers[userIndex]; - EntityID entityID = m_World->CreateEntity(); - ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform"); - transform["Position"] = glm::vec3(-1.5f, 0.f, 0.f); - ComponentWrapper model = m_World->AttachComponent(entityID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.mesh"; - model["Color"] = glm::vec4(rand()%255 / 255.f, rand()%255 / 255.f, rand() %255 / 255.f, 1.f); - ComponentWrapper player = m_World->AttachComponent(entityID, "Player"); - m_PlayerDefinitions[playerIndex].EntityID = entityID; - return; - } - } - LOG_WARNING("Server is full!"); - -} - void Server::kick(PlayerID player) { disconnect(player); @@ -415,10 +373,10 @@ void Server::kick(PlayerID player) PlayerID Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) { - for (int i = 0; i < m_MaxConnections; i++) { - if (m_PlayerDefinitions[i].Endpoint.address() == endpoint.address() && - m_PlayerDefinitions[i].Endpoint.port() == endpoint.port()) { - return i; + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == endpoint.address() && + kv.second.Endpoint.port() == endpoint.port()) { + return kv.first; } } return -1; @@ -443,9 +401,54 @@ bool Server::OnInputCommand(const Events::InputCommand & e) bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e) { + m_ConnectedPlayers[e.PlayerID].EntityID = e.Player.ID; + Packet packet = Packet(MessageType::OnPlayerSpawned); packet.WritePrimitive(e.Player.ID); packet.WritePrimitive(e.Spawner.ID); + // We don't send PlayerID here because it will always be set to -1 + packet.WriteString(m_ConnectedPlayers[e.PlayerID].Name); send(e.PlayerID, packet); return false; } + +bool Server::OnEntityDeleted(const Events::EntityDeleted & e) +{ + if (!e.Cascaded) { + Packet packet = Packet(MessageType::EntityDeleted); + packet.WritePrimitive(e.DeletedEntity); + broadcast(packet); + } + return false; +} + +bool Server::OnComponentDeleted(const Events::ComponentDeleted & e) +{ + if (!e.Cascaded) { + Packet packet = Packet(MessageType::ComponentDeleted); + packet.WritePrimitive(e.Entity); + packet.WriteString(e.ComponentType); + broadcast(packet); + } + return false; +} + +void Server::parsePlayerTransform(Packet& packet) +{ + glm::vec3 position; + glm::vec3 orientation; + position.x = packet.ReadPrimitive(); + position.y = packet.ReadPrimitive(); + position.z = packet.ReadPrimitive(); + orientation.x = packet.ReadPrimitive(); + orientation.y = packet.ReadPrimitive(); + orientation.z = packet.ReadPrimitive(); + + PlayerID playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); + EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID); + + if (player.Valid()) { + player["Transform"]["Position"] = position; + player["Transform"]["Orientation"] = orientation; + } +} diff --git a/src/Engine/Rendering/AnimationSystem.cpp b/src/Engine/Rendering/AnimationSystem.cpp new file mode 100644 index 00000000..2126362f --- /dev/null +++ b/src/Engine/Rendering/AnimationSystem.cpp @@ -0,0 +1,47 @@ +#include "Rendering/AnimationSystem.h" + +void AnimationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& animationComponent, double dt) +{ + if(!entity.HasComponent("Model")) { + return; + } + + Model* model; + try { + model = ResourceManager::Load<::Model, true>(entity["Model"]["Resource"]); + } catch (const std::exception&) { + return; + } + + + Skeleton* skeleton = model->m_RawModel->m_Skeleton; + const Skeleton::Animation* animation = skeleton->GetAnimation(animationComponent["Name"]); + + if(animation != nullptr) { + double animationSpeed = (double)animationComponent["Speed"]; + + if (animationSpeed != 0.0) { + double nextTime = (double)animationComponent["Time"] + animationSpeed * dt; + + + if (!(bool)animationComponent["Loop"] && glm::abs(nextTime) > animation->Duration) { + (double&)animationComponent["Time"] = glm::sign(nextTime) * animation->Duration; + (double&)animationComponent["Speed"] = 0.0; + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["Name"]; + m_EventBroker->Publish(e); + } else { + if (glm::abs(nextTime) > animation->Duration) { + (double&)animationComponent["Time"] = glm::abs(nextTime) - animation->Duration; + } else { + (double&)animationComponent["Time"] = nextTime; + } + } + } + } + + + +} + diff --git a/src/Engine/Rendering/DrawFinalPass.cpp b/src/Engine/Rendering/DrawFinalPass.cpp index ca2a9d77..0cd0e576 100644 --- a/src/Engine/Rendering/DrawFinalPass.cpp +++ b/src/Engine/Rendering/DrawFinalPass.cpp @@ -75,6 +75,10 @@ void DrawFinalPass::Draw(RenderScene& scene) glUniform4fv(glGetUniformLocation(shaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color)); glUniform4fv(glGetUniformLocation(shaderHandle, "DiffuseColor"), 1, glm::value_ptr(modelJob->DiffuseColor)); + glUniform4fv(glGetUniformLocation(shaderHandle, "FillColor"), 1, glm::value_ptr(modelJob->FillColor)); + glUniform1f(glGetUniformLocation(shaderHandle, "FillPercentage"), modelJob->FillPercentage); + + glActiveTexture(GL_TEXTURE0); if(modelJob->DiffuseTexture != nullptr) { glBindTexture(GL_TEXTURE_2D, modelJob->DiffuseTexture->m_Texture); @@ -96,12 +100,9 @@ void DrawFinalPass::Draw(RenderScene& scene) //TODO: Fixa så att modelsJobs kan spela upp olika animationer och så att den kan få in en tid istället för 1.0f - Hälsningar Johan och Andreas :) if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { - auto animation = modelJob->Model->m_RawModel->m_Skeleton->GetAnimation("running"); - if (animation != nullptr) { - std::vector frameBones = modelJob->Model->m_RawModel->m_Skeleton->GetFrameBones( - *animation, - 0.0f - ); + + if (modelJob->Animation != nullptr) { + std::vector frameBones = modelJob->Skeleton->GetFrameBones( *modelJob->Animation, modelJob->AnimationTime); glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); } } diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index f063b344..c7e23479 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -48,7 +48,7 @@ void PickingPass::Draw(RenderScene& scene) PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle()); //TODO: Render: Add code for more jobs than modeljobs. - GLuint ShaderHandle = m_PickingProgram->GetHandle(); + GLuint shaderHandle = m_PickingProgram->GetHandle(); m_PickingProgram->Bind(); if (scene.ClearDepth) { @@ -75,22 +75,30 @@ void PickingPass::Draw(RenderScene& scene) m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]); if (m_ColorCounter[0] > 255) { m_ColorCounter[0] = 0; - m_ColorCounter[1]++; + m_ColorCounter[1] += 5; } else { - m_ColorCounter[0]++; + m_ColorCounter[0] += 50; } } m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + + if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { + + if (modelJob->Animation != nullptr) { + std::vector frameBones = modelJob->Skeleton->GetFrameBones(*modelJob->Animation, modelJob->AnimationTime); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); + } + } glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); - glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex); + glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } } diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index 58bbd1ee..a67f2c75 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -4,6 +4,7 @@ RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRender : System(world, eventBroker) , m_Renderer(renderer) , m_RenderFrame(renderFrame) + , m_World(world) { EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); @@ -51,6 +52,12 @@ void RenderSystem::fillModels(std::list>& jobs) // Don't render the local player if (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) { + if (!entity.HasComponent("HealthHUD") && entity.Name() != "Crosshair" && entity.Name() != "Weapon") { //Should work but needs to be fixed. Should only render the things "childed" to the local player camera + continue; + } + } + + if ((entity.Name() == "Weapon" || entity.HasComponent("HealthHUD")) && !entity.IsChildOf(m_LocalPlayer)) { continue; } @@ -68,9 +75,21 @@ void RenderSystem::fillModels(std::list>& jobs) } } + + float fillPercentage = 0.f; + glm::vec4 fillColor = glm::vec4(0); + + if(m_World->HasComponent(modelComponent.EntityID, "Fill")) { + auto fillComponent = m_World->GetComponent(modelComponent.EntityID, "Fill"); + fillPercentage = (float)(double)fillComponent["Percentage"]; + fillColor = (glm::vec4)fillComponent["Color"]; + } + + + glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, m_World); for (auto matGroup : model->MaterialGroups()) { - std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, m_World)); + std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, m_World, fillColor, fillPercentage)); jobs.push_back(modelJob); } } @@ -156,8 +175,8 @@ void RenderSystem::fillText(std::list>& jobs, World* } glm::mat4 modelMatrix = Transform::ModelMatrix(textComponent.EntityID, world); - std::shared_ptr modelJob = std::shared_ptr(new TextJob(modelMatrix, font, textComponent)); - jobs.push_back(modelJob); + std::shared_ptr textJob = std::shared_ptr(new TextJob(modelMatrix, font, textComponent)); + jobs.push_back(textJob); } } @@ -177,7 +196,6 @@ void RenderSystem::Update(double dt) m_Camera->SetOrientation(Transform::AbsoluteOrientation(m_CurrentCamera)); } - //Only supports opaque geometry atm RenderScene scene; scene.Camera = m_Camera; diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 384c4a5b..903b270f 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -10,6 +10,9 @@ #include "Core/EntityFileWriter.h" #include "Game/Systems/CapturePointSystem.h" #include "Game/Systems/WeaponSystem.h" +#include "Game/Systems/PlayerHUD.h" +#include "Game/Systems/LifetimeSystem.h" +#include "../Engine/Rendering/AnimationSystem.h" Game::Game(int argc, char* argv[]) { @@ -57,7 +60,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("Debug.LoadMap", ""); if (!mapToLoad.empty()) { auto file = ResourceManager::Load(mapToLoad); @@ -83,9 +86,13 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); + m_SystemPipeline->AddSystem(updateOrderLevel); // Populate Octree with collidables ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + // Collision and TriggerSystem should update after player. ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 5e5be33e..9e118070 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -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 @@ -46,10 +45,16 @@ void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& comp } } -bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e) +bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) { - //save the changed HP to a vector. it will be taken care of in UpdateComponent - m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerDamagedID, -e.DamageAmount)); + ComponentWrapper cHealth = e.Player["Health"]; + double& health = cHealth["Health"]; + health -= e.Damage; + + if (health <= 0.0) { + m_World->DeleteEntity(e.Player.ID); + } + return true; } diff --git a/src/Game/Systems/InterpolationSystem.cpp b/src/Game/Systems/InterpolationSystem.cpp index 2b6e82f2..bfb6952a 100644 --- a/src/Game/Systems/InterpolationSystem.cpp +++ b/src/Game/Systems/InterpolationSystem.cpp @@ -12,6 +12,11 @@ InterpolationSystem::InterpolationSystem(World* world, EventBroker* eventBroker) void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& transform, double dt) { + // Don't interpolate entities that might already have been removed + if (!entity.Valid()) { + return; + } + if (m_NextTransform.find(transform.EntityID) != m_NextTransform.end()) { // Exists in map m_NextTransform[transform.EntityID].interpolationTime += dt; Transform sTransform = m_NextTransform[transform.EntityID]; @@ -31,11 +36,10 @@ void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrappe // Position glm::vec3 nextPosition = sTransform.Position; glm::vec3 currentPosition = static_cast(transform["Position"]); - // HACK: Hardcoded tolerance value for player position desync = 1 - if (isLocalPlayer && glm::length(nextPosition - currentPosition) < 1.f) { - return; + // HACK: Don't force position for players + if (!isLocalPlayer) { + (glm::vec3&)transform["Position"] += vectorInterpolation(currentPosition, nextPosition, sTransform.interpolationTime); } - (glm::vec3&)transform["Position"] += vectorInterpolation(currentPosition, nextPosition, sTransform.interpolationTime); // Orientation // Don't force orientation for players if (!isLocalPlayer) { diff --git a/src/Game/Systems/LifetimeSystem.cpp b/src/Game/Systems/LifetimeSystem.cpp new file mode 100644 index 00000000..db19e88a --- /dev/null +++ b/src/Game/Systems/LifetimeSystem.cpp @@ -0,0 +1,27 @@ +#include "Systems/LifetimeSystem.h" + +void LifetimeSystem::Update(double dt) +{ + for (auto& e : m_Deletions) { + m_World->DeleteEntity(e.ID); + } + m_Deletions.clear(); +} + +void LifetimeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cLifetime, double dt) +{ + if (dt == 0.0) { + return; + } + + if (!entity.Valid()) { + return; + } + + double& lifetime = cLifetime["Lifetime"]; + lifetime -= dt; + + if (lifetime <= 0.0) { + m_Deletions.push_back(entity); + } +} \ No newline at end of file diff --git a/src/Game/Systems/PlayerHUD.cpp b/src/Game/Systems/PlayerHUD.cpp new file mode 100644 index 00000000..55d0c3f1 --- /dev/null +++ b/src/Game/Systems/PlayerHUD.cpp @@ -0,0 +1,57 @@ +#include "Game/Systems/PlayerHUD.h" + +PlayerHUD::PlayerHUD(World* world, EventBroker* eventBrokerer) + :System(world, eventBrokerer) + , m_World(world) + , m_EventBroker(eventBrokerer) +{ + + +} + +PlayerHUD::~PlayerHUD() +{ + + +} + +void PlayerHUD::Update(double dt) +{ + auto healthHUDs = m_World->GetComponents("HealthHUD"); + if (healthHUDs == nullptr) { + return; + } + + for (auto& healthHUDComponent : *healthHUDs) { + EntityWrapper entity = EntityWrapper(m_World, healthHUDComponent.EntityID); + + EntityWrapper entityIDParent = entity; + while (entityIDParent.Parent().Valid()) { + entityIDParent = entityIDParent.Parent(); + + if (entityIDParent.HasComponent("Health")) { + break; + } + } + + if (entityIDParent.HasComponent("Health")) { + + if (entity.HasComponent("Text")) { + std::string s = ""; + s = s + std::to_string((int)(double)entityIDParent["Health"]["Health"]); + s = s + "/"; + s = s + std::to_string((int)(double)entityIDParent["Health"]["MaxHealth"]); + float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; + (glm::vec4&)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 1.f); + entity["Text"]["Content"] = s; + } + + if(entity.HasComponent("Fill")) { + float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; + (glm::vec4&)entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 0.f); + (double&)entity["Fill"]["Percentage"] = healthPercentage; + + } + } + } +} diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index 5c75a673..0f6588be 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -36,15 +36,18 @@ void PlayerMovementSystem::Update(double dt) glm::vec3& ori = cTransform["Orientation"]; ori.y += controller->Rotation().y; + float playerMovementSpeed = player["Player"]["MovementSpeed"]; + float playerCrouchSpeed = player["Player"]["CrouchSpeed"]; + if (player.HasComponent("Physics")) { ComponentWrapper cPhysics = player["Physics"]; glm::vec3 wishDirection = controller->Movement() * glm::inverse(glm::quat(ori)); float wishSpeed; if (controller->Crouching()) { - wishSpeed = player["Player"]["CrouchSpeed"]; + wishSpeed = playerCrouchSpeed; } else { - wishSpeed = player["Player"]["MovementSpeed"]; + wishSpeed = playerMovementSpeed; } glm::vec3& velocity = cPhysics["Velocity"]; ImGui::Text("velocity: (%f, %f, %f)", velocity.x, velocity.y, velocity.z); @@ -85,8 +88,33 @@ void PlayerMovementSystem::Update(double dt) // size = glm::vec3(1.f, 1.6f, 1.f); // } //} + + // Animations + EntityWrapper playerModel = player.FirstChildByName("PlayerModel"); + if (playerModel.Valid()) { + ComponentWrapper cAnimation = playerModel["Animation"]; + + float movementLength = glm::length(groundVelocity); + if (glm::length(controller->Movement()) > 0.f) { + if (controller->Crouching()) { + cAnimation["Name"] = "Crouch Walk"; + (double&)cAnimation["Speed"] = 1.f * -glm::sign(controller->Movement().z); + } else { + cAnimation["Name"] = "Run"; + (double&)cAnimation["Speed"] = 2.f * -glm::sign(controller->Movement().z); + } + } else { + if (controller->Crouching()) { + cAnimation["Name"] = "Crouch"; + (double&)cAnimation["Speed"] = 1.f; + } else { + cAnimation["Name"] = "Hold Pos"; + (double&)cAnimation["Speed"] = 1.f; + } + } + } } - + controller->Reset(); } } diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 84dce05c..7db6e8ff 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -102,10 +102,10 @@ bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) } // TODO: Set the player name to whatever - //EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); - //if (playerName.Valid()) { - // playerName["Text"]["Content"] = ???; - //} + EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); + if (playerName.Valid()) { + playerName["Text"]["Content"] = e.PlayerName; + } return true; } \ No newline at end of file diff --git a/src/Game/Systems/SpawnerSystem.cpp b/src/Game/Systems/SpawnerSystem.cpp index 8cb5b99d..f9357113 100644 --- a/src/Game/Systems/SpawnerSystem.cpp +++ b/src/Game/Systems/SpawnerSystem.cpp @@ -50,7 +50,8 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent / // Set its position and orientation to that of the SpawnPoint spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); - spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint.World, spawnPoint.ID)); + // TODO: Quaternions, bitch + //spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint.World, spawnPoint.ID)); return spawnedEntity; } diff --git a/src/Game/Systems/WeaponSystem.cpp b/src/Game/Systems/WeaponSystem.cpp index 11f76490..f78e092f 100644 --- a/src/Game/Systems/WeaponSystem.cpp +++ b/src/Game/Systems/WeaponSystem.cpp @@ -5,49 +5,127 @@ WeaponSystem::WeaponSystem(World* world, EventBroker* eventBroker, IRenderer* re , ImpureSystem() , m_Renderer(renderer) { + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &WeaponSystem::OnPlayerSpawned); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EShoot, &WeaponSystem::OnShoot); } void WeaponSystem::Update(double dt) { - for (int i = m_EShootVector.size(); i > 0; i--) - { - //TODO: check if player has enough ammo and if weapon has a cooldown or not - //pick the object - PickData pickDataFromShot = m_Renderer->Pick(std::get<1>(m_EShootVector[i - 1])); - if (pickDataFromShot.Entity == EntityID_Invalid) { - m_EShootVector.erase(m_EShootVector.begin() + i - 1); - continue; - } - //if its a player, do PlayerDamage event - const bool hasPlayerComponent = m_World->HasComponent(pickDataFromShot.Entity, "Player"); - if (hasPlayerComponent) { - Events::PlayerDamage ePlayerDamage; - //TODO: damage based on weapontype/class? - //TODO: multiple shots at the same time? (shotgunner) - ePlayerDamage.DamageAmount = 25; - ePlayerDamage.PlayerDamagedID = pickDataFromShot.Entity; - m_EventBroker->Publish(ePlayerDamage); - } - m_EShootVector.erase(m_EShootVector.begin() + i - 1); +} + +bool WeaponSystem::OnPlayerSpawned(const Events::PlayerSpawned& e) +{ + if (e.PlayerID == -1) { + m_LocalPlayer = e.Player; } + return true; } bool WeaponSystem::OnInputCommand(const Events::InputCommand& e) { if (e.Command == "PrimaryFire" && e.Value > 0) { Events::Shoot eShoot; - eShoot.shooter = e.PlayerID; + if (e.PlayerID == -1) { + eShoot.Player = m_LocalPlayer; + } else { + eShoot.Player = e.Player; + } m_EventBroker->Publish(eShoot); } + return true; } -bool WeaponSystem::OnShoot(const Events::Shoot& e) { - //screen center, based on current resolution! + +bool WeaponSystem::OnShoot(Events::Shoot& eShoot) +{ + if (!eShoot.Player.Valid()) { + return false; + } + + // TODO: Weapon firing effects here + + auto rayRed = ResourceManager::Load("Schema/Entities/RayRed.xml"); + auto rayBlue = ResourceManager::Load("Schema/Entities/RayBlue.xml"); + + EntityWrapper weapon = eShoot.Player.FirstChildByName("WeaponMuzzle"); + if (weapon.Valid()) { + EntityWrapper ray; + if ((ComponentInfo::EnumType)eShoot.Player["Team"]["Team"] == eShoot.Player["Team"]["Team"].Enum("Red")) { + EntityFileParser parser(rayRed); + EntityID rayID = parser.MergeEntities(m_World); + ray = EntityWrapper(m_World, rayID); + } else { + EntityFileParser parser(rayBlue); + EntityID rayID = parser.MergeEntities(m_World); + ray = EntityWrapper(m_World, rayID); + } + + glm::mat4 transformation = Transform::AbsoluteTransformation(weapon); + glm::vec3 scale; + glm::vec3 translation; + glm::quat orientation; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(transformation, scale, orientation, translation, skew, perspective); + + // Matrix to euler angles + glm::vec3 euler; + euler.y = glm::asin(-transformation[0][2]); + if (cos(euler.y) != 0) { + euler.x = atan2(transformation[1][2], transformation[2][2]); + euler.z = atan2(transformation[0][1], transformation[0][0]); + } else { + euler.x = atan2(-transformation[2][0], transformation[1][1]); + euler.z = 0; + } + + //LOG_DEBUG("rotation: %f %f %f", euler.x, euler.y, euler.z); + (glm::vec3&)ray["Transform"]["Position"] = translation; + (glm::vec3&)ray["Transform"]["Orientation"] = euler; + //(glm::vec3&)ray["Transform"]["Orientation"] = Transform::AbsoluteOrientationEuler(weapon); + } + + // Only run further picking code client-side! + if (eShoot.Player != m_LocalPlayer) { + return false; + } + + // 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); - m_EShootVector.push_back(std::make_pair(e.shooter, centerScreen)); + + // TODO: check if player has enough ammo and if weapon has a cooldown or not + + // Pick middle of screen + PickData pickData = m_Renderer->Pick(centerScreen); + if (pickData.Entity == EntityID_Invalid) { + return false; + } + + EntityWrapper player(m_World, pickData.Entity); + + // Only care about players being hit + if (!player.HasComponent("Player")) { + player = player.FirstParentWithComponent("Player"); + } + if (!player.Valid()) { + return false; + } + + // Check for friendly fire + EntityWrapper shooter = eShoot.Player; + if ((ComponentInfo::EnumType)player["Team"]["Team"] == (ComponentInfo::EnumType)shooter["Team"]["Team"]) { + return false; + } + + // TODO: Weapon damage calculations etc + Events::PlayerDamage ePlayerDamage; + ePlayerDamage.Player = player; + ePlayerDamage.Damage = 100; + m_EventBroker->Publish(ePlayerDamage); + return true; -} \ No newline at end of file +}