Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Source/Game-Lib/Game-Lib/ECS/Components/ProximityTrigger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once
#include <Base/Types.h>

#include <Meta/Generated/Game/ProximityTriggerEnum.h>
DECLARE_GENERIC_BITWISE_OPERATORS(Generated::ProximityTriggerFlagEnum);

#include <entt/entt.hpp>
#include <set>

namespace ECS::Components
{
struct ProximityTrigger
{
static const u32 INVALID_NETWORK_ID = std::numeric_limits<u32>().max();

u32 networkID = INVALID_NETWORK_ID;
Generated::ProximityTriggerFlagEnum flags = Generated::ProximityTriggerFlagEnum::None;
std::set<entt::entity> playersInside; // Entities currently inside the trigger
};
}
2 changes: 2 additions & 0 deletions Source/Game-Lib/Game-Lib/ECS/Components/Tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ namespace ECS::Components
struct AnimatingTag {};
struct UnitRebuildSkinTexture {};
struct UnitRebuildGeosets {};
struct LocalPlayerTag {};
struct PlayerTag {};
}
6 changes: 5 additions & 1 deletion Source/Game-Lib/Game-Lib/ECS/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@
#include "Game-Lib/ECS/Singletons/DayNightCycle.h"
#include "Game-Lib/ECS/Singletons/EngineStats.h"
#include "Game-Lib/ECS/Singletons/JoltState.h"
#include "Game-Lib/ECS/Singletons/ProximityTriggerSingleton.h"
#include "Game-Lib/ECS/Singletons/RenderState.h"
#include "Game-Lib/ECS/Systems/Animation.h"
#include "Game-Lib/ECS/Systems/UpdateAreaLights.h"
#include "Game-Lib/ECS/Systems/CalculateCameraMatrices.h"
#include "Game-Lib/ECS/Systems/CalculateShadowCameraMatrices.h"
#include "Game-Lib/ECS/Systems/CalculateTransformMatrices.h"
#include "Game-Lib/ECS/Systems/UpdateDayNightCycle.h"
#include "Game-Lib/ECS/Systems/DrawDebugMesh.h"
#include "Game-Lib/ECS/Systems/FreeflyingCamera.h"
#include "Game-Lib/ECS/Systems/OrbitalCamera.h"
#include "Game-Lib/ECS/Systems/NetworkConnection.h"
#include "Game-Lib/ECS/Systems/ProximityTriggers.h"
#include "Game-Lib/ECS/Systems/UpdateUnitEntities.h"
#include "Game-Lib/ECS/Systems/UpdatePhysics.h"
#include "Game-Lib/ECS/Systems/UpdateScripts.h"
#include "Game-Lib/ECS/Systems/UpdateSkyboxes.h"
#include "Game-Lib/ECS/Systems/CalculateTransformMatrices.h"
#include "Game-Lib/ECS/Systems/UpdateAABBs.h"
#include "Game-Lib/ECS/Systems/CharacterController.h"
#include "Game-Lib/ECS/Systems/UI/HandleInput.h"
Expand Down Expand Up @@ -63,6 +65,7 @@ namespace ECS
entt::registry::context& ctx = gameRegistry.ctx();
ctx.emplace<Singletons::EngineStats>();
ctx.emplace<Singletons::RenderState>();
ctx.emplace<Singletons::ProximityTriggerSingleton>();

// UI
entt::registry& uiRegistry = *registries.uiRegistry;
Expand Down Expand Up @@ -99,6 +102,7 @@ namespace ECS
Systems::CalculateTransformMatrices::Update(gameRegistry, clampedDeltaTime);
Systems::UpdateAABBs::Update(gameRegistry, clampedDeltaTime);
Systems::UpdatePhysics::Update(gameRegistry, clampedDeltaTime);
Systems::ProximityTriggers::Update(gameRegistry, clampedDeltaTime);

// Note: For now UpdateScripts should always be run last
Systems::UpdateScripts::Update(gameRegistry, clampedDeltaTime);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include <Base/Types.h>

#include <entt/entity/fwd.hpp>
#include <RTree/RTree.h>
#include <robinhood/robinhood.h>

namespace ECS::Singletons
{
struct ProximityTriggerSingleton
{
public:
RTree<entt::entity, f32, 3> proximityTriggers;

robin_hood::unordered_map<entt::entity, robin_hood::unordered_set<entt::entity>> entityToProximityTriggers; // Maps entity to the triggers it is currently inside
robin_hood::unordered_map<u32, entt::entity> triggerIDToEntity; // Maps trigger ID to entity
};
}
5 changes: 5 additions & 0 deletions Source/Game-Lib/Game-Lib/ECS/Systems/CharacterController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Game-Lib/ECS/Components/Model.h"
#include "Game-Lib/ECS/Components/MovementInfo.h"
#include "Game-Lib/ECS/Components/Name.h"
#include "Game-Lib/ECS/Components/Tags.h"
#include "Game-Lib/ECS/Components/Unit.h"
#include "Game-Lib/ECS/Components/UnitCustomization.h"
#include "Game-Lib/ECS/Components/UnitEquipment.h"
Expand Down Expand Up @@ -689,6 +690,7 @@ namespace ECS::Systems

auto& name = registry.get_or_emplace<Components::Name>(characterSingleton.moverEntity);
auto& aabb = registry.get_or_emplace<Components::AABB>(characterSingleton.moverEntity);
registry.emplace_or_replace<Components::WorldAABB>(characterSingleton.moverEntity);
auto& transform = registry.get_or_emplace<Components::Transform>(characterSingleton.moverEntity);
auto& moverModel = registry.get_or_emplace<Components::Model>(characterSingleton.moverEntity);
auto& movementInfo = registry.get_or_emplace<Components::MovementInfo>(characterSingleton.moverEntity);
Expand Down Expand Up @@ -720,7 +722,10 @@ namespace ECS::Systems

ModelLoader* modelLoader = ServiceLocator::GetGameRenderer()->GetModelLoader();
modelLoader->LoadDisplayIDForEntity(characterSingleton.moverEntity, moverModel, Database::Unit::DisplayInfoType::Creature, 50);

registry.emplace_or_replace<Components::PlayerTag>(characterSingleton.moverEntity);
}
registry.emplace_or_replace<Components::LocalPlayerTag>(characterSingleton.moverEntity);

f32 width = 0.4166f;
f32 height = 1.9134f;
Expand Down
68 changes: 68 additions & 0 deletions Source/Game-Lib/Game-Lib/ECS/Systems/NetworkConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@
#include "Game-Lib/ECS/Components/MovementInfo.h"
#include "Game-Lib/ECS/Components/Name.h"
#include "Game-Lib/ECS/Components/Item.h"
#include "Game-Lib/ECS/Components/ProximityTrigger.h"
#include "Game-Lib/ECS/Components/Tags.h"
#include "Game-Lib/ECS/Components/Unit.h"
#include "Game-Lib/ECS/Components/UnitCustomization.h"
#include "Game-Lib/ECS/Components/UnitEquipment.h"
#include "Game-Lib/ECS/Components/UnitMovementOverTime.h"
#include "Game-Lib/ECS/Components/UnitStatsComponent.h"
#include "Game-Lib/ECS/Singletons/CharacterSingleton.h"
#include "Game-Lib/ECS/Singletons/NetworkState.h"
#include "Game-Lib/ECS/Singletons/ProximityTriggerSingleton.h"
#include "Game-Lib/ECS/Singletons/Database/ClientDBSingleton.h"
#include "Game-Lib/ECS/Util/MessageBuilderUtil.h"
#include "Game-Lib/ECS/Util/ProximityTriggerUtil.h"
#include "Game-Lib/ECS/Util/Transforms.h"
#include "Game-Lib/Rendering/GameRenderer.h"
#include "Game-Lib/Rendering/Model/ModelLoader.h"
Expand All @@ -33,6 +37,8 @@

#include <Gameplay/Network/GameMessageRouter.h>

#include <Meta/Generated/Game/ProximityTriggerEnum.h>

#include <Network/Client.h>
#include <Network/Define.h>

Expand Down Expand Up @@ -180,6 +186,7 @@ namespace ECS::Systems

entt::entity newEntity = registry->create();
registry->emplace<Components::AABB>(newEntity);
registry->emplace<Components::WorldAABB>(newEntity);
registry->emplace<Components::Transform>(newEntity);
registry->emplace<Components::Name>(newEntity);
registry->emplace<Components::Model>(newEntity);
Expand All @@ -196,6 +203,11 @@ namespace ECS::Systems
unit.networkID = networkID;
unit.targetEntity = entt::null;

if (unit.networkID.GetType() == GameDefine::ObjectGuid::Type::Player)
{
registry->emplace_or_replace<Components::PlayerTag>(newEntity);
}

TransformSystem& transformSystem = TransformSystem::Get(*registry);
transformSystem.SetWorldPosition(newEntity, position);
transformSystem.SetWorldRotation(newEntity, rotation);
Expand Down Expand Up @@ -1127,6 +1139,48 @@ namespace ECS::Systems
}
return true;
}
bool HandleOnTriggerCreate(Network::SocketID socketID, Network::Message& message)
{
u32 triggerID;
std::string name;
Generated::ProximityTriggerFlagEnum flags;
u16 mapID;
vec3 position;
vec3 extents;

if (!message.buffer->GetU32(triggerID))
return false;

if (!message.buffer->GetString(name))
return false;

if (!message.buffer->Get(flags))
return false;

if (!message.buffer->GetU16(mapID))
return false;

if (!message.buffer->Get(position))
return false;

if (!message.buffer->Get(extents))
return false;

entt::registry& registry = *ServiceLocator::GetEnttRegistries()->gameRegistry;
ECS::Util::ProximityTriggerUtil::CreateTrigger(registry, triggerID, name, flags, mapID, position, extents);
return true;
}
bool HandleOnTriggerDestroy(Network::SocketID socketID, Network::Message& message)
{
u32 triggerID;

if (!message.buffer->GetU32(triggerID))
return false;

entt::registry& registry = *ServiceLocator::GetEnttRegistries()->gameRegistry;
ECS::Util::ProximityTriggerUtil::DestroyTrigger(registry, triggerID);
return true;
}

void NetworkConnection::Init(entt::registry& registry)
{
Expand Down Expand Up @@ -1166,6 +1220,9 @@ namespace ECS::Systems

networkState.gameMessageRouter->SetMessageHandler(Network::GameOpcode::Server_SendSpellCastResult, Network::GameMessageHandler(Network::ConnectionStatus::Connected, 0u, -1, &HandleOnSpellCastResult));
networkState.gameMessageRouter->SetMessageHandler(Network::GameOpcode::Server_SendCombatEvent, Network::GameMessageHandler(Network::ConnectionStatus::Connected, 0u, -1, &HandleOnCombatEvent));

networkState.gameMessageRouter->SetMessageHandler(Network::GameOpcode::Server_TriggerCreate, Network::GameMessageHandler(Network::ConnectionStatus::Connected, 0u, -1, &HandleOnTriggerCreate));
networkState.gameMessageRouter->SetMessageHandler(Network::GameOpcode::Server_TriggerDestroy, Network::GameMessageHandler(Network::ConnectionStatus::Connected, 0u, -1, &HandleOnTriggerDestroy));
}
}

Expand Down Expand Up @@ -1265,6 +1322,17 @@ namespace ECS::Systems
registry.destroy(entity);
}

// Clean up any networked proximity triggers
auto& proximityTriggerSingleton = ctx.get<Singletons::ProximityTriggerSingleton>();
auto triggerView = registry.view<Components::ProximityTrigger>();
triggerView.each([&](entt::entity triggerEntity, Components::ProximityTrigger& proximityTrigger)
{
if (proximityTrigger.networkID == Components::ProximityTrigger::INVALID_NETWORK_ID)
return;

proximityTriggerSingleton.proximityTriggers.Remove(triggerEntity);
});

networkState.lastPingTime = 0u;
networkState.lastPongTime = 0u;
networkState.ping = 0;
Expand Down
153 changes: 153 additions & 0 deletions Source/Game-Lib/Game-Lib/ECS/Systems/ProximityTriggers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "ProximityTriggers.h"

#include "Game-Lib/ECS/Components/AABB.h"
#include "Game-Lib/ECS/Components/Events.h"
#include "Game-Lib/ECS/Components/Name.h"
#include "Game-Lib/ECS/Components/ProximityTrigger.h"
#include "Game-Lib/ECS/Components/Tags.h"
#include "Game-Lib/ECS/Singletons/CharacterSingleton.h"
#include "Game-Lib/ECS/Singletons/NetworkState.h"
#include "Game-Lib/ECS/Singletons/ProximityTriggerSingleton.h"
#include "Game-Lib/ECS/Util/EventUtil.h"
#include "Game-Lib/ECS/Util/MessageBuilderUtil.h"
#include "Game-Lib/ECS/Util/Transforms.h"
#include "Game-Lib/Scripting/LuaDefines.h"
#include "Game-Lib/Scripting/LuaManager.h"
#include "Game-Lib/Scripting/Handlers/TriggerEventHandler.h"

#include <Base/Memory/Bytebuffer.h>
#include <Base/Util/DebugHandler.h>

#include <Network/Client.h>

#include <entt/entt.hpp>
#include <tracy/Tracy.hpp>


namespace ECS::Systems
{
bool IsTransformInAABB(const Components::Transform& transform, const Components::WorldAABB& aabb)
{
const vec3& p = transform.GetWorldPosition();

// Center/half-extents form reduces comparisons against two bounds to one abs compare per axis.
const vec3 center = (aabb.min + aabb.max) * 0.5f;
const vec3 half = (aabb.max - aabb.min) * 0.5f;

const vec3 d = glm::abs(p - center); // component-wise abs
return (d.x <= half.x) && (d.y <= half.y) && (d.z <= half.z);
}

void OnEnter(Scripting::TriggerEventHandler* triggerEventHandler, Scripting::LuaManager* luaManager, ECS::Singletons::NetworkState& networkState, entt::entity triggerEntity, Components::ProximityTrigger& trigger, entt::entity playerEntity)
{
// This is an optimization so the server doesn't need to repeatedly test all triggers for all players
if ((trigger.flags & Generated::ProximityTriggerFlagEnum::IsServerAuthorative) != Generated::ProximityTriggerFlagEnum::None)
{
// Serverside event for sure
std::shared_ptr<Bytebuffer> buffer = Bytebuffer::Borrow<16>();

if (!ECS::Util::MessageBuilder::ProximityTrigger::BuildProximityTriggerEnter(buffer, trigger.networkID))
return;

networkState.client->Send(buffer);
}

// Clientside event
Scripting::LuaTriggerEventOnTriggerEnterData eventData =
{
.triggerID = entt::to_integral(triggerEntity),
.playerID = entt::to_integral(playerEntity)
};
triggerEventHandler->CallEvent(luaManager->GetInternalState(), static_cast<u32>(Generated::LuaTriggerEventEnum::OnEnter), &eventData);
}

void OnExit(Scripting::TriggerEventHandler* triggerEventHandler, Scripting::LuaManager* luaManager, ECS::Singletons::NetworkState& networkState, entt::entity triggerEntity, Components::ProximityTrigger& trigger, entt::entity playerEntity)
{
Scripting::LuaTriggerEventOnTriggerExitData eventData =
{
.triggerID = entt::to_integral(triggerEntity),
.playerID = entt::to_integral(playerEntity)
};
triggerEventHandler->CallEvent(luaManager->GetInternalState(), static_cast<u32>(Generated::LuaTriggerEventEnum::OnExit), &eventData);
}

void OnStay(Scripting::TriggerEventHandler* triggerEventHandler, Scripting::LuaManager* luaManager, ECS::Singletons::NetworkState& networkState, entt::entity triggerEntity, Components::ProximityTrigger& trigger, entt::entity playerEntity)
{
Scripting::LuaTriggerEventOnTriggerStayData eventData =
{
.triggerID = entt::to_integral(triggerEntity),
.playerID = entt::to_integral(playerEntity)
};
triggerEventHandler->CallEvent(luaManager->GetInternalState(), static_cast<u32>(Generated::LuaTriggerEventEnum::OnStay), &eventData);
}

void ProximityTriggers::Update(entt::registry& registry, f32 deltaTime)
{
ZoneScopedN("ECS::ProximityTriggers");

entt::registry::context& ctx = registry.ctx();
auto& proximityTriggerSingleton = ctx.get<ECS::Singletons::ProximityTriggerSingleton>();

auto view = registry.view<Components::Transform, Components::WorldAABB, Components::DirtyTransform, Components::LocalPlayerTag>();
auto dirtyTriggerView = registry.view<Components::WorldAABB, Components::ProximityTrigger, Components::DirtyTransform>();

// Update all dirty triggers in the RTree
dirtyTriggerView.each([&](entt::entity triggerEntity, Components::WorldAABB& triggerAABB, Components::ProximityTrigger& proximityTrigger, Components::DirtyTransform&)
{
proximityTriggerSingleton.proximityTriggers.Remove(triggerEntity);
proximityTriggerSingleton.proximityTriggers.Insert(reinterpret_cast<f32*>(&triggerAABB.min), reinterpret_cast<f32*>(&triggerAABB.max), triggerEntity);
});

auto& characterSingleton = ctx.get<ECS::Singletons::CharacterSingleton>();

entt::entity playerEntity = characterSingleton.moverEntity;

if (playerEntity == entt::null || !registry.valid(playerEntity))
{
return;
}

auto* luaManager = ServiceLocator::GetLuaManager();
auto* triggerEventHandler = luaManager->GetLuaHandler<Scripting::TriggerEventHandler*>(Scripting::LuaHandlerType::TriggerEvent);
auto& playerAABB = registry.get<Components::WorldAABB>(playerEntity);
auto& networkState = ctx.get<ECS::Singletons::NetworkState>();

// Get a COPY of the set of proximity triggers this player is inside of
auto& triggerList = proximityTriggerSingleton.entityToProximityTriggers[playerEntity];
auto previousTriggerList = triggerList;

proximityTriggerSingleton.proximityTriggers.Search(reinterpret_cast<f32*>(&playerAABB.min), reinterpret_cast<f32*>(&playerAABB.max), [&](const entt::entity triggerEntity) -> bool
{
auto& proximityTrigger = registry.get<Components::ProximityTrigger>(triggerEntity);
auto& playersInTrigger = proximityTrigger.playersInside;

bool wasInside = proximityTrigger.playersInside.contains(playerEntity);
if (wasInside)
{
OnStay(triggerEventHandler, luaManager, networkState, triggerEntity, proximityTrigger, playerEntity);
}
else
{
OnEnter(triggerEventHandler, luaManager, networkState, triggerEntity, proximityTrigger, playerEntity);

proximityTrigger.playersInside.insert(playerEntity);
triggerList.insert(triggerEntity);
}

// Remove the trigger from the set of triggers exited
previousTriggerList.erase(triggerEntity);
return true;
});

// Now we have a set of triggers that the player was inside of, but is no longer
for (const auto triggerEntity : previousTriggerList)
{
auto& proximityTrigger = registry.get<Components::ProximityTrigger>(triggerEntity);

OnExit(triggerEventHandler, luaManager, networkState, triggerEntity, proximityTrigger, playerEntity);

proximityTrigger.playersInside.erase(playerEntity);
triggerList.erase(triggerEntity);
}
}
}
Loading
Loading