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
21 changes: 21 additions & 0 deletions Resources/Database/Updates/Update_2025_08_12_2222.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
DROP TABLE IF EXISTS public.proximity_triggers CASCADE;

CREATE TABLE public.proximity_triggers
(
id serial NOT NULL,
name text NOT NULL DEFAULT '',
flags smallint NOT NULL DEFAULT '0',
map_id smallint NOT NULL DEFAULT '0',
position_x real NOT NULL DEFAULT '0.0',
position_y real NOT NULL DEFAULT '0.0',
position_z real NOT NULL DEFAULT '0.0',
extents_x real NOT NULL DEFAULT '1.0',
extents_y real NOT NULL DEFAULT '1.0',
extents_z real NOT NULL DEFAULT '1.0',
PRIMARY KEY (id)
)

TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.proximity_triggers
OWNER to postgres;
2 changes: 1 addition & 1 deletion Source/Server-Common/Server-Common.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
local mod = Solution.Util.CreateModuleTable("Server-Common", { "base", "fileformat", "input", "network", "gameplay", "luau-compiler", "luau-vm", "enkits", "refl-cpp", "utfcpp", "base64", "libpqxx" })
local mod = Solution.Util.CreateModuleTable("Server-Common", { "base", "fileformat", "meta", "input", "network", "gameplay", "luau-compiler", "luau-vm", "enkits", "refl-cpp", "utfcpp", "base64", "libpqxx" })

Solution.Util.CreateStaticLib(mod.Name, Solution.Projects.Current.BinDir, mod.Dependencies, function()
local defines = { "_CRT_SECURE_NO_WARNINGS", "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS", "_SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS" }
Expand Down
20 changes: 20 additions & 0 deletions Source/Server-Common/Server-Common/Database/Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include <Gameplay/GameDefine.h>

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

#include <robinhood/robinhood.h>

#include <entt/fwd.hpp>
Expand Down Expand Up @@ -288,6 +290,17 @@ namespace Database
u16 slot = 0;
};

struct ProximityTrigger
{
public:
u32 id = 0;
std::string name = "";
Generated::ProximityTriggerFlagEnum flags = Generated::ProximityTriggerFlagEnum::None;
u16 mapID = 0;
vec3 position = vec3(0.0f, 0.0f, 0.0f);
vec3 extents = vec3(1.0f, 1.0f, 1.0f);
};

struct PermissionTables
{
public:
Expand Down Expand Up @@ -322,4 +335,11 @@ namespace Database
robin_hood::unordered_map<u64, std::vector<u16>> charIDToPermissionGroups;
robin_hood::unordered_map<u64, std::vector<CharacterCurrency>> charIDToCurrency;
};

struct ProximityTriggersTables
{
public:
std::vector<ProximityTrigger> triggers;
robin_hood::unordered_map<u32, entt::entity> triggerIDToEntity;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "ProximityTriggerUtils.h"
#include "Server-Common/Database/DBController.h"

#include <Base/Util/DebugHandler.h>
#include <Base/Util/StringUtils.h>

#include <pqxx/nontransaction>

namespace Database::Util::ProximityTrigger
{
namespace Loading
{
void InitProximityTriggersTablesPreparedStatements(std::shared_ptr<DBConnection>& dbConnection)
{
NC_LOG_INFO("Loading Prepared Statements Proximity Triggers Tables...");

try
{
dbConnection->connection->prepare("ProximityTriggerCreate", "INSERT INTO public.proximity_triggers (name, flags, map_id, position_x, position_y, position_z, extents_x, extents_y, extents_z) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id");
dbConnection->connection->prepare("ProximityTriggerDelete", "DELETE FROM public.proximity_triggers WHERE id = $1");

NC_LOG_INFO("Loaded Prepared Statements Proximity Triggers Tables\n");
}
catch (const pqxx::sql_error& e)
{
NC_LOG_CRITICAL("{0}", e.what());
return;
}
}

void LoadProximityTriggersTables(std::shared_ptr<DBConnection>& dbConnection, Database::ProximityTriggersTables& proximityTriggersTables)
{
NC_LOG_INFO("-- Loading Proximity Triggers Tables --");

u64 totalRows = 0;
totalRows += LoadProximityTriggers(dbConnection, proximityTriggersTables);

NC_LOG_INFO("-- Loaded Proximity Triggers Tables ({0} rows) --\n", totalRows);
}

u64 LoadProximityTriggers(std::shared_ptr<DBConnection>& dbConnection, Database::ProximityTriggersTables& proximityTriggersTables)
{
NC_LOG_INFO("Loading Table 'proximity_triggers'");

pqxx::nontransaction nonTransaction = dbConnection->NewNonTransaction();

auto result = nonTransaction.exec("SELECT COUNT(*) FROM public.proximity_triggers");
u64 numRows = result[0][0].as<u64>();

proximityTriggersTables.triggers.clear();

if (numRows == 0)
{
NC_LOG_INFO("Skipped Table 'proximity_triggers'");
return 0;
}

proximityTriggersTables.triggers.reserve(numRows);

nonTransaction.for_stream("SELECT * FROM public.proximity_triggers", [&proximityTriggersTables](u32 id, const std::string& name, u16 flags, u16 mapID, f32 positionX, f32 positionY, f32 positionZ, f32 extentsX, f32 extentsY, f32 extentsZ)
{
Database::ProximityTrigger trigger =
{
.id = id,
.name = name,
.flags = static_cast<Generated::ProximityTriggerFlagEnum>(flags),
.mapID = mapID,
.position = vec3(positionX, positionY, positionZ),
.extents = vec3(extentsX, extentsY, extentsZ)
};
proximityTriggersTables.triggers.push_back(trigger);
});

NC_LOG_INFO("Loaded Table 'proximity_triggers' ({0} Rows)", numRows);
return numRows;
}
}

bool ProximityTriggerCreate(pqxx::work& transaction, const std::string& name, u16 flags, u16 mapID, const vec3& position, const vec3& extents, u32& triggerID)
{
try
{
auto queryResult = transaction.exec(pqxx::prepped("ProximityTriggerCreate"), pqxx::params{ name, flags, mapID, position.x, position.y, position.z, extents.x, extents.y, extents.z });
if (queryResult.empty())
return false;

triggerID = queryResult[0][0].as<u32>();
return true;
}
catch (const pqxx::sql_error& e)
{
NC_LOG_WARNING("{0}", e.what());
return false;
}
}

bool ProximityTriggerDelete(pqxx::work& transaction, u32 triggerID)
{
try
{
auto queryResult = transaction.exec(pqxx::prepped("ProximityTriggerDelete"), pqxx::params{ triggerID });
if (queryResult.affected_rows() == 0)
return false;

return true;
}
catch (const pqxx::sql_error& e)
{
NC_LOG_WARNING("{0}", e.what());
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once
#include "Server-Common/Database/Definitions.h"

#include <Base/Types.h>

#include <pqxx/pqxx>

namespace Database
{
struct DBConnection;

namespace Util::ProximityTrigger
{
namespace Loading
{
void InitProximityTriggersTablesPreparedStatements(std::shared_ptr<DBConnection>& dbConnection);

void LoadProximityTriggersTables(std::shared_ptr<DBConnection>& dbConnection, Database::ProximityTriggersTables& proximityTriggersTables);
u64 LoadProximityTriggers(std::shared_ptr<DBConnection>& dbConnection, Database::ProximityTriggersTables& proximityTriggersTables);
}

bool ProximityTriggerCreate(pqxx::work& transaction, const std::string& name, u16 flags, u16 mapID, const vec3& position, const vec3& extents, u32& triggerID);
bool ProximityTriggerDelete(pqxx::work& transaction, u32 triggerID);
}
}
17 changes: 17 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Components/AABB.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once
#include <Base/Types.h>

namespace ECS::Components
{
struct AABB
{
vec3 centerPos;
vec3 extents;
};

struct WorldAABB
{
vec3 min;
vec3 max;
};
}
22 changes: 22 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Components/ProximityTrigger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <Base/Types.h>

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

#include <entt/entt.hpp>
#include <robinhood/robinhood.h>

namespace ECS::Components
{
struct ProximityTrigger
{
public:
u32 triggerID;
std::string name;
Generated::ProximityTriggerFlagEnum flags = Generated::ProximityTriggerFlagEnum::None;
robin_hood::unordered_set<entt::entity> playersInside; // Entities currently inside the trigger
robin_hood::unordered_set<entt::entity> playersEntered; // Entities that just entered
robin_hood::unordered_set<entt::entity> playersExited; // Entities that just entered
};
}
4 changes: 4 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Components/Tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ namespace ECS::Tags
struct CharacterNeedsDeinitialization {};
struct CharacterNeedsContainerUpdate {};
struct CharacterNeedsDisplayUpdate {};

struct ProximityTriggerNeedsInitialization {};
struct ProximityTriggerIsServerSideOnly {};
struct ProximityTriggerIsClientSide {};
}
3 changes: 3 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Server-Game/ECS/Systems/CharacterUpdate.h"
#include "Server-Game/ECS/Systems/DatabaseSetup.h"
#include "Server-Game/ECS/Systems/NetworkConnection.h"
#include "Server-Game/ECS/Systems/ProximityTrigger.h"
#include "Server-Game/ECS/Systems/Replication.h"
#include "Server-Game/ECS/Systems/UpdatePower.h"
#include "Server-Game/ECS/Systems/UpdateScripts.h"
Expand All @@ -30,6 +31,7 @@ namespace ECS
Systems::CharacterLoginHandler::Init(registry);
Systems::CharacterInitialization::Init(registry);
Systems::CharacterUpdate::Init(registry);
Systems::ProximityTrigger::Init(registry);
Systems::UpdatePower::Init(registry);
Systems::UpdateSpell::Init(registry);
Systems::UpdateScripts::Init(registry);
Expand All @@ -43,6 +45,7 @@ namespace ECS
Systems::CharacterLoginHandler::Update(registry, deltaTime);
Systems::CharacterInitialization::Update(registry, deltaTime);
Systems::CharacterUpdate::Update(registry, deltaTime);
Systems::ProximityTrigger::Update(registry, deltaTime);
Systems::UpdatePower::Update(registry, deltaTime);
Systems::UpdateSpell::Update(registry, deltaTime);
Systems::Replication::Update(registry, deltaTime);
Expand Down
1 change: 1 addition & 0 deletions Source/Server-Game/Server-Game/ECS/Singletons/GameCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace ECS
Database::CurrencyTables currencyTables;
Database::ItemTables itemTables;
Database::CharacterTables characterTables;
Database::ProximityTriggersTables proximityTriggerTables;
};
}

Expand Down
22 changes: 22 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Singletons/ProximityTriggers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <Base/Types.h>

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

namespace ECS
{
namespace Singletons
{
struct ProximityTriggers
{
public:
robin_hood::unordered_map<u32, entt::entity> triggerIDToEntity;
robin_hood::unordered_set<u32> triggerIDsToDestroy;

robin_hood::unordered_set<u32> activeTriggers;
RTree<u32, f32, 3> activeTriggersVisTree;
};
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#include "CharacterInitialization.h"

#include "Server-Game/ECS/Components/AABB.h"
#include "Server-Game/ECS/Components/CastInfo.h"
#include "Server-Game/ECS/Components/CharacterInfo.h"
#include "Server-Game/ECS/Components/DisplayInfo.h"
#include "Server-Game/ECS/Components/NetInfo.h"
#include "Server-Game/ECS/Components/ObjectInfo.h"
#include "Server-Game/ECS/Components/PlayerContainers.h"
#include "Server-Game/ECS/Components/ProximityTrigger.h"
#include "Server-Game/ECS/Components/Tags.h"
#include "Server-Game/ECS/Components/TargetInfo.h"
#include "Server-Game/ECS/Components/UnitStatsComponent.h"
Expand Down Expand Up @@ -134,6 +136,9 @@ namespace ECS::Systems
transform.mapID = databaseResult[0][10].as<u16>();
transform.position = vec3(databaseResult[0][11].as<f32>(), databaseResult[0][12].as<f32>(), databaseResult[0][13].as<f32>());

auto& aabb = registry.emplace<Components::AABB>(entity);
aabb.extents = vec3(3.0f, 5.0f, 2.0f); // TODO: Create proper data for this

f32 orientation = databaseResult[0][14].as<f32>();
transform.rotation = quat(vec3(0.0f, orientation, 0.0f));

Expand Down Expand Up @@ -231,8 +236,27 @@ namespace ECS::Systems
}
}
}
}
registry.emplace_or_replace<Tags::CharacterNeedsContainerUpdate>(entity);

for (auto& trigger : gameCache.proximityTriggerTables.triggers)
{
if (!gameCache.proximityTriggerTables.triggerIDToEntity.contains(trigger.id))
continue;

entt::entity triggerEntity = gameCache.proximityTriggerTables.triggerIDToEntity[trigger.id];
if (registry.all_of<Tags::ProximityTriggerIsServerSideOnly>(triggerEntity))
continue;

auto& triggerTransform = registry.get<Components::Transform>(triggerEntity);
auto& triggerAABB = registry.get<Components::AABB>(triggerEntity);
auto& proximityTrigger = registry.get<Components::ProximityTrigger>(triggerEntity);

if (triggerTransform.mapID != transform.mapID)
continue;

registry.emplace_or_replace<Tags::CharacterNeedsContainerUpdate>(entity);
if (!Util::MessageBuilder::ProximityTrigger::BuildProximityTriggerCreate(buffer, trigger.id, trigger.name, trigger.flags, triggerTransform.mapID, triggerTransform.position, triggerAABB.extents))
continue;
}

if (failed)
Expand Down
3 changes: 3 additions & 0 deletions Source/Server-Game/Server-Game/ECS/Systems/DatabaseSetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <Server-Common/Database/Util/CurrencyUtils.h>
#include <Server-Common/Database/Util/ItemUtils.h>
#include <Server-Common/Database/Util/PermissionUtils.h>
#include <Server-Common/Database/Util/ProximityTriggerUtils.h>

#include <Base/Util/DebugHandler.h>
#include <Base/Util/JsonUtils.h>
Expand Down Expand Up @@ -70,11 +71,13 @@ namespace ECS::Systems
Database::Util::Currency::InitCurrencyTablesPreparedStatements(characterConnection);
Database::Util::Item::Loading::InitItemTablesPreparedStatements(characterConnection);
Database::Util::Character::Loading::InitCharacterTablesPreparedStatements(characterConnection);
Database::Util::ProximityTrigger::Loading::InitProximityTriggersTablesPreparedStatements(characterConnection);

Database::Util::Permission::LoadPermissionTables(characterConnection, gameCache.permissionTables);
Database::Util::Currency::LoadCurrencyTables(characterConnection, gameCache.currencyTables);
Database::Util::Item::Loading::LoadItemTables(characterConnection, gameCache.itemTables);
Database::Util::Character::Loading::LoadCharacterTables(characterConnection, gameCache.characterTables, gameCache.itemTables);
Database::Util::ProximityTrigger::Loading::LoadProximityTriggersTables(characterConnection, gameCache.proximityTriggerTables);
}
}

Expand Down
Loading
Loading