mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-13 18:24:20 +00:00
Compare commits
12 Commits
issue-1339
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90db1ac699 | ||
|
|
9f8d300340 | ||
|
|
1e9b18fa9d | ||
|
|
e5b8e5c6b7 | ||
|
|
707880b5fc | ||
|
|
90607bdd5c | ||
|
|
bb8f569354 | ||
|
|
93076dc36d | ||
|
|
a307f0601a | ||
|
|
045e097b13 | ||
|
|
ca0da9d3bf | ||
|
|
1d2de705fb |
@@ -1,105 +1,77 @@
|
||||
#include "Metrics.hpp"
|
||||
#include "Metrics.h"
|
||||
|
||||
#include "StringifiedEnum.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
std::unordered_map<MetricVariable, Metric*> Metrics::m_Metrics = {};
|
||||
std::vector<MetricVariable> Metrics::m_Variables = {
|
||||
MetricVariable::GameLoop,
|
||||
MetricVariable::PacketHandling,
|
||||
MetricVariable::UpdateEntities,
|
||||
MetricVariable::UpdateSpawners,
|
||||
MetricVariable::Physics,
|
||||
MetricVariable::UpdateReplica,
|
||||
MetricVariable::Ghosting,
|
||||
MetricVariable::CPUTime,
|
||||
MetricVariable::Sleep,
|
||||
MetricVariable::Frame,
|
||||
};
|
||||
namespace {
|
||||
std::unordered_map<MetricVariable, Metric> g_Metrics = {};
|
||||
std::vector<MetricVariable> g_Variables = {
|
||||
MetricVariable::GameLoop,
|
||||
MetricVariable::PacketHandling,
|
||||
MetricVariable::UpdateEntities,
|
||||
MetricVariable::UpdateSpawners,
|
||||
MetricVariable::Physics,
|
||||
MetricVariable::UpdateReplica,
|
||||
MetricVariable::Ghosting,
|
||||
MetricVariable::CPUTime,
|
||||
MetricVariable::Sleep,
|
||||
MetricVariable::Frame,
|
||||
};
|
||||
}
|
||||
|
||||
void Metrics::AddMeasurement(MetricVariable variable, int64_t value) {
|
||||
const auto& iter = m_Metrics.find(variable);
|
||||
|
||||
Metric* metric;
|
||||
|
||||
if (iter == m_Metrics.end()) {
|
||||
metric = new Metric();
|
||||
|
||||
m_Metrics[variable] = metric;
|
||||
} else {
|
||||
metric = iter->second;
|
||||
}
|
||||
auto& metric = g_Metrics[variable];
|
||||
|
||||
AddMeasurement(metric, value);
|
||||
}
|
||||
|
||||
void Metrics::AddMeasurement(Metric* metric, int64_t value) {
|
||||
const auto index = metric->measurementIndex;
|
||||
void Metrics::AddMeasurement(Metric& metric, int64_t value) {
|
||||
const auto index = metric.measurementIndex;
|
||||
|
||||
metric->measurements[index] = value;
|
||||
metric.measurements[index] = value;
|
||||
|
||||
if (metric->max == -1 || value > metric->max) {
|
||||
metric->max = value;
|
||||
} else if (metric->min == -1 || metric->min > value) {
|
||||
metric->min = value;
|
||||
if (metric.max == -1 || value > metric.max) {
|
||||
metric.max = value;
|
||||
} else if (metric.min == -1 || metric.min > value) {
|
||||
metric.min = value;
|
||||
}
|
||||
|
||||
if (metric->measurementSize < MAX_MEASURMENT_POINTS) {
|
||||
metric->measurementSize++;
|
||||
if (metric.measurementSize < MAX_MEASURMENT_POINTS) {
|
||||
metric.measurementSize++;
|
||||
}
|
||||
|
||||
metric->measurementIndex = (index + 1) % MAX_MEASURMENT_POINTS;
|
||||
metric.measurementIndex = (index + 1) % MAX_MEASURMENT_POINTS;
|
||||
}
|
||||
|
||||
const Metric* Metrics::GetMetric(MetricVariable variable) {
|
||||
const auto& iter = m_Metrics.find(variable);
|
||||
|
||||
if (iter == m_Metrics.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Metric* metric = iter->second;
|
||||
const Metric& Metrics::GetMetric(MetricVariable variable) {
|
||||
auto& metric = g_Metrics[variable];
|
||||
|
||||
int64_t average = 0;
|
||||
|
||||
for (size_t i = 0; i < metric->measurementSize; i++) {
|
||||
average += metric->measurements[i];
|
||||
for (size_t i = 0; i < metric.measurementSize; i++) {
|
||||
average += metric.measurements[i];
|
||||
}
|
||||
|
||||
average /= metric->measurementSize;
|
||||
average /= metric.measurementSize;
|
||||
|
||||
metric->average = average;
|
||||
metric.average = average;
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
void Metrics::StartMeasurement(MetricVariable variable) {
|
||||
const auto& iter = m_Metrics.find(variable);
|
||||
auto& metric = g_Metrics[variable];
|
||||
|
||||
Metric* metric;
|
||||
|
||||
if (iter == m_Metrics.end()) {
|
||||
metric = new Metric();
|
||||
|
||||
m_Metrics[variable] = metric;
|
||||
} else {
|
||||
metric = iter->second;
|
||||
}
|
||||
|
||||
metric->activeMeasurement = std::chrono::high_resolution_clock::now();
|
||||
metric.activeMeasurement = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void Metrics::EndMeasurement(MetricVariable variable) {
|
||||
const auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
const auto& iter = m_Metrics.find(variable);
|
||||
auto& metric = g_Metrics[variable];
|
||||
|
||||
if (iter == m_Metrics.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Metric* metric = iter->second;
|
||||
|
||||
const auto elapsed = end - metric->activeMeasurement;
|
||||
const auto elapsed = end - metric.activeMeasurement;
|
||||
|
||||
const auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
|
||||
|
||||
@@ -110,44 +82,12 @@ float Metrics::ToMiliseconds(int64_t nanoseconds) {
|
||||
return static_cast<float>(nanoseconds) / 1e6;
|
||||
}
|
||||
|
||||
std::string Metrics::MetricVariableToString(MetricVariable variable) {
|
||||
switch (variable) {
|
||||
case MetricVariable::GameLoop:
|
||||
return "GameLoop";
|
||||
case MetricVariable::PacketHandling:
|
||||
return "PacketHandling";
|
||||
case MetricVariable::UpdateEntities:
|
||||
return "UpdateEntities";
|
||||
case MetricVariable::UpdateSpawners:
|
||||
return "UpdateSpawners";
|
||||
case MetricVariable::Physics:
|
||||
return "Physics";
|
||||
case MetricVariable::UpdateReplica:
|
||||
return "UpdateReplica";
|
||||
case MetricVariable::Sleep:
|
||||
return "Sleep";
|
||||
case MetricVariable::CPUTime:
|
||||
return "CPUTime";
|
||||
case MetricVariable::Frame:
|
||||
return "Frame";
|
||||
case MetricVariable::Ghosting:
|
||||
return "Ghosting";
|
||||
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
const std::string_view Metrics::MetricVariableToString(MetricVariable variable) {
|
||||
return StringifiedEnum::ToString(variable);
|
||||
}
|
||||
|
||||
const std::vector<MetricVariable>& Metrics::GetAllMetrics() {
|
||||
return m_Variables;
|
||||
}
|
||||
|
||||
void Metrics::Clear() {
|
||||
for (const auto& pair : m_Metrics) {
|
||||
delete pair.second;
|
||||
}
|
||||
|
||||
m_Metrics.clear();
|
||||
return g_Variables;
|
||||
}
|
||||
|
||||
/* RSS Memory utilities
|
||||
|
||||
48
dCommon/Metrics.h
Normal file
48
dCommon/Metrics.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
|
||||
#define MAX_MEASURMENT_POINTS 1024
|
||||
|
||||
enum class MetricVariable : int32_t {
|
||||
GameLoop,
|
||||
PacketHandling,
|
||||
UpdateEntities,
|
||||
UpdateSpawners,
|
||||
Physics,
|
||||
UpdateReplica,
|
||||
Ghosting,
|
||||
CPUTime,
|
||||
Sleep,
|
||||
Frame,
|
||||
};
|
||||
|
||||
struct Metric {
|
||||
int64_t measurements[MAX_MEASURMENT_POINTS] = {};
|
||||
size_t measurementIndex = 0;
|
||||
size_t measurementSize = 0;
|
||||
int64_t max = -1;
|
||||
int64_t min = -1;
|
||||
int64_t average = 0;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> activeMeasurement;
|
||||
};
|
||||
|
||||
namespace Metrics {
|
||||
void AddMeasurement(MetricVariable variable, int64_t value);
|
||||
void AddMeasurement(Metric& metric, int64_t value);
|
||||
const Metric& GetMetric(MetricVariable variable);
|
||||
void StartMeasurement(MetricVariable variable);
|
||||
void EndMeasurement(MetricVariable variable);
|
||||
float ToMiliseconds(int64_t nanoseconds);
|
||||
const std::string_view MetricVariableToString(MetricVariable variable);
|
||||
const std::vector<MetricVariable>& GetAllMetrics();
|
||||
|
||||
size_t GetPeakRSS();
|
||||
size_t GetCurrentRSS();
|
||||
size_t GetProcessID();
|
||||
};
|
||||
@@ -1,61 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "dCommonVars.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
|
||||
#define MAX_MEASURMENT_POINTS 1024
|
||||
|
||||
enum class MetricVariable : int32_t
|
||||
{
|
||||
GameLoop,
|
||||
PacketHandling,
|
||||
UpdateEntities,
|
||||
UpdateSpawners,
|
||||
Physics,
|
||||
UpdateReplica,
|
||||
Ghosting,
|
||||
CPUTime,
|
||||
Sleep,
|
||||
Frame,
|
||||
};
|
||||
|
||||
struct Metric
|
||||
{
|
||||
int64_t measurements[MAX_MEASURMENT_POINTS] = {};
|
||||
size_t measurementIndex = 0;
|
||||
size_t measurementSize = 0;
|
||||
int64_t max = -1;
|
||||
int64_t min = -1;
|
||||
int64_t average = 0;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> activeMeasurement;
|
||||
};
|
||||
|
||||
class Metrics
|
||||
{
|
||||
public:
|
||||
~Metrics();
|
||||
|
||||
static void AddMeasurement(MetricVariable variable, int64_t value);
|
||||
static void AddMeasurement(Metric* metric, int64_t value);
|
||||
static const Metric* GetMetric(MetricVariable variable);
|
||||
static void StartMeasurement(MetricVariable variable);
|
||||
static void EndMeasurement(MetricVariable variable);
|
||||
static float ToMiliseconds(int64_t nanoseconds);
|
||||
static std::string MetricVariableToString(MetricVariable variable);
|
||||
static const std::vector<MetricVariable>& GetAllMetrics();
|
||||
|
||||
static size_t GetPeakRSS();
|
||||
static size_t GetCurrentRSS();
|
||||
static size_t GetProcessID();
|
||||
|
||||
static void Clear();
|
||||
|
||||
private:
|
||||
Metrics();
|
||||
|
||||
static std::unordered_map<MetricVariable, Metric*> m_Metrics;
|
||||
static std::vector<MetricVariable> m_Variables;
|
||||
};
|
||||
@@ -427,7 +427,7 @@ void Entity::Initialize() {
|
||||
comp->SetMaxArmor(destCompData[0].armor);
|
||||
comp->SetDeathBehavior(destCompData[0].death_behavior);
|
||||
|
||||
comp->SetIsSmashable(destCompData[0].isSmashable);
|
||||
comp->SetIsSmashable(comp->GetIsSmashable() || destCompData[0].isSmashable);
|
||||
|
||||
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
|
||||
comp->SetCurrencyIndex(destCompData[0].CurrencyIndex);
|
||||
@@ -1613,8 +1613,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
|
||||
else Game::entityManager->DestroyEntity(this);
|
||||
}
|
||||
|
||||
const auto& grpNameQBShowBricks = GetVar<std::string>(u"grpNameQBShowBricks");
|
||||
|
||||
const auto& grpNameQBShowBricks = GetVarAsString(u"grpNameQBShowBricks");
|
||||
if (!grpNameQBShowBricks.empty()) {
|
||||
auto spawners = Game::zoneManager->GetSpawnersByName(grpNameQBShowBricks);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "SkillComponent.h"
|
||||
#include "SwitchComponent.h"
|
||||
#include "UserManager.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "dZoneManager.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "Game.h"
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#include "DluAssert.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
#include "Metrics.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace LeaderboardManager {
|
||||
std::map<GameID, Leaderboard::Type> leaderboardCache;
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
#include "CharacterComponent.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "eMissionTaskType.h"
|
||||
#include <ranges>
|
||||
|
||||
namespace {
|
||||
std::unique_ptr<Trade> g_EmptyTrade;
|
||||
}
|
||||
|
||||
TradingManager* TradingManager::m_Address = nullptr;
|
||||
|
||||
@@ -233,55 +238,38 @@ void Trade::SendUpdateToOther(LWOOBJID participant) {
|
||||
GameMessages::SendServerTradeUpdate(other->GetObjectID(), coins, items, other->GetSystemAddress());
|
||||
}
|
||||
|
||||
TradingManager::TradingManager() {
|
||||
}
|
||||
|
||||
TradingManager::~TradingManager() {
|
||||
for (const auto& pair : trades) {
|
||||
delete pair.second;
|
||||
}
|
||||
|
||||
trades.clear();
|
||||
}
|
||||
|
||||
Trade* TradingManager::GetTrade(LWOOBJID tradeId) const {
|
||||
const std::unique_ptr<Trade>& TradingManager::GetTrade(LWOOBJID tradeId) const {
|
||||
const auto& pair = trades.find(tradeId);
|
||||
|
||||
if (pair == trades.end()) return nullptr;
|
||||
if (pair == trades.end()) return g_EmptyTrade;
|
||||
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
Trade* TradingManager::GetPlayerTrade(LWOOBJID playerId) const {
|
||||
for (const auto& pair : trades) {
|
||||
if (pair.second->IsParticipant(playerId)) {
|
||||
return pair.second;
|
||||
const std::unique_ptr<Trade>& TradingManager::GetPlayerTrade(LWOOBJID playerId) const {
|
||||
for (const auto& trade : trades | std::views::values) {
|
||||
if (trade->IsParticipant(playerId)) {
|
||||
return trade;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_EmptyTrade;
|
||||
}
|
||||
|
||||
void TradingManager::CancelTrade(const LWOOBJID canceller, LWOOBJID tradeId, const bool sendCancelMessage) {
|
||||
auto* trade = GetTrade(tradeId);
|
||||
const auto& trade = GetTrade(tradeId);
|
||||
|
||||
if (trade == nullptr) return;
|
||||
|
||||
if (sendCancelMessage) trade->Cancel(canceller);
|
||||
|
||||
delete trade;
|
||||
|
||||
trades.erase(tradeId);
|
||||
}
|
||||
|
||||
Trade* TradingManager::NewTrade(LWOOBJID participantA, LWOOBJID participantB) {
|
||||
void TradingManager::NewTrade(LWOOBJID participantA, LWOOBJID participantB) {
|
||||
const LWOOBJID tradeId = ObjectIDManager::GenerateObjectID();
|
||||
|
||||
auto* trade = new Trade(tradeId, participantA, participantB);
|
||||
|
||||
trades[tradeId] = trade;
|
||||
trades.insert_or_assign(tradeId, std::make_unique<Trade>(tradeId, participantA, participantB));
|
||||
|
||||
LOG("Created new trade between (%llu) <-> (%llu)", participantA, participantB);
|
||||
|
||||
return trade;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
struct TradeItem
|
||||
{
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
struct TradeItem {
|
||||
LWOOBJID itemId;
|
||||
LOT itemLot;
|
||||
uint32_t itemCount;
|
||||
};
|
||||
|
||||
class Trade
|
||||
{
|
||||
class Trade {
|
||||
public:
|
||||
explicit Trade(LWOOBJID tradeId, LWOOBJID participantA, LWOOBJID participantB);
|
||||
~Trade();
|
||||
@@ -50,8 +51,7 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class TradingManager
|
||||
{
|
||||
class TradingManager {
|
||||
public:
|
||||
static TradingManager* Instance() {
|
||||
if (!m_Address) {
|
||||
@@ -61,16 +61,13 @@ public:
|
||||
return m_Address;
|
||||
}
|
||||
|
||||
explicit TradingManager();
|
||||
~TradingManager();
|
||||
|
||||
Trade* GetTrade(LWOOBJID tradeId) const;
|
||||
Trade* GetPlayerTrade(LWOOBJID playerId) const;
|
||||
const std::unique_ptr<Trade>& GetTrade(LWOOBJID tradeId) const;
|
||||
const std::unique_ptr<Trade>& GetPlayerTrade(LWOOBJID playerId) const;
|
||||
void CancelTrade(const LWOOBJID canceller, LWOOBJID tradeId, const bool sendCancelMessage = true);
|
||||
Trade* NewTrade(LWOOBJID participantA, LWOOBJID participantB);
|
||||
void NewTrade(LWOOBJID participantA, LWOOBJID participantB);
|
||||
|
||||
private:
|
||||
static TradingManager* m_Address; //For singleton method
|
||||
|
||||
std::unordered_map<LWOOBJID, Trade*> trades;
|
||||
std::unordered_map<LWOOBJID, std::unique_ptr<Trade>> trades;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "eMatchUpdate.h"
|
||||
#include "ServiceType.h"
|
||||
#include "MessageType/Chat.h"
|
||||
#include "ObjectIDManager.h"
|
||||
|
||||
#include "CDCurrencyTableTable.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
@@ -29,6 +30,11 @@
|
||||
#include "LeaderboardManager.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "Amf3.h"
|
||||
#include <ranges>
|
||||
|
||||
namespace {
|
||||
const ActivityInstance g_EmptyInstance{ nullptr, CDActivities{} };
|
||||
}
|
||||
|
||||
ActivityComponent::ActivityComponent(Entity* parent, int32_t componentID) : Component(parent, componentID) {
|
||||
RegisterMsg(&ActivityComponent::OnGetObjectReportInfo);
|
||||
@@ -71,9 +77,9 @@ void ActivityComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsIniti
|
||||
if (m_DirtyActivityInfo) {
|
||||
outBitStream.Write<uint32_t>(m_ActivityPlayers.size());
|
||||
if (!m_ActivityPlayers.empty()) {
|
||||
for (const auto& activityPlayer : m_ActivityPlayers) {
|
||||
outBitStream.Write<LWOOBJID>(activityPlayer->playerID);
|
||||
for (const auto& activityValue : activityPlayer->values) {
|
||||
for (const auto& [playerID, values] : m_ActivityPlayers) {
|
||||
outBitStream.Write<LWOOBJID>(playerID);
|
||||
for (const auto& activityValue : values) {
|
||||
outBitStream.Write<float_t>(activityValue);
|
||||
}
|
||||
}
|
||||
@@ -111,78 +117,81 @@ void ActivityComponent::PlayerJoin(Entity* player) {
|
||||
if (HasLobby()) {
|
||||
PlayerJoinLobby(player);
|
||||
} else if (!IsPlayedBy(player)) {
|
||||
auto* instance = NewInstance();
|
||||
instance->AddParticipant(player);
|
||||
NewInstance().AddParticipant(player);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerJoinLobby(Entity* player) {
|
||||
if (!m_Parent->HasComponent(eReplicaComponentType::QUICK_BUILD))
|
||||
GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby
|
||||
LobbyPlayer* newLobbyPlayer = new LobbyPlayer();
|
||||
newLobbyPlayer->entityID = player->GetObjectID();
|
||||
Lobby* playerLobby = nullptr;
|
||||
LobbyPlayer newLobbyPlayer{};
|
||||
newLobbyPlayer.entityID = player->GetObjectID();
|
||||
LWOOBJID playerLobbyID = LWOOBJID_EMPTY;
|
||||
|
||||
auto* character = player->GetCharacter();
|
||||
if (character != nullptr)
|
||||
character->SetLastNonInstanceZoneID(Game::zoneManager->GetZone()->GetWorldID());
|
||||
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
if (lobby->players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() < m_ActivityInfo.maxTeams) {
|
||||
for (auto& [lobbyID, lobby] : m_Queue) {
|
||||
if (lobby.players.size() < m_ActivityInfo.maxTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby.players.size() < m_ActivityInfo.maxTeams) {
|
||||
// If an empty slot in an existing lobby is found
|
||||
lobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby = lobby;
|
||||
lobby.players.push_back(newLobbyPlayer);
|
||||
playerLobbyID = lobbyID;
|
||||
|
||||
// Update the joining player on players already in the lobby, and update players already in the lobby on the joining player
|
||||
std::string matchUpdateJoined = "player=9:" + std::to_string(player->GetObjectID()) + "\nplayerName=0:" + player->GetCharacter()->GetName();
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
LDFData<LWOOBJID> playerLDF("player", player->GetObjectID());
|
||||
LDFData<std::string> playerName("playerName", player->GetCharacter()->GetName());
|
||||
std::string matchUpdateJoined = playerLDF.GetString() + "\n" + playerName.GetString();
|
||||
for (const auto& joinedPlayer : lobby.players) {
|
||||
auto* const entity = joinedPlayer.GetEntity();
|
||||
|
||||
if (entity == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string matchUpdate = "player=9:" + std::to_string(entity->GetObjectID()) + "\nplayerName=0:" + entity->GetCharacter()->GetName();
|
||||
LDFData<LWOOBJID> entityLDF("player", entity->GetObjectID());
|
||||
LDFData<std::string> entityName("playerName", entity->GetCharacter()->GetName());
|
||||
std::string matchUpdate = entityLDF.GetString() + "\n" + entityName.GetString();
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchUpdate, eMatchUpdate::PLAYER_ADDED);
|
||||
PlayerReady(entity, joinedPlayer->ready);
|
||||
PlayerReady(entity, joinedPlayer.ready);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateJoined, eMatchUpdate::PLAYER_ADDED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!playerLobby) {
|
||||
if (playerLobbyID == LWOOBJID_EMPTY) {
|
||||
// If all lobbies are full
|
||||
playerLobby = new Lobby();
|
||||
playerLobby->players.push_back(newLobbyPlayer);
|
||||
playerLobby->timer = m_ActivityInfo.waitTime / 1000;
|
||||
m_Queue.push_back(playerLobby);
|
||||
playerLobbyID = ObjectIDManager::GenerateObjectID();
|
||||
auto& newLobby = m_Queue[playerLobbyID];
|
||||
newLobby.players.push_back(newLobbyPlayer);
|
||||
newLobby.timer = m_ActivityInfo.waitTime / 1000;
|
||||
}
|
||||
const auto& lobby = m_Queue[playerLobbyID];
|
||||
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && playerLobby->players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && playerLobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby.players.size() >= m_ActivityInfo.minTeamSize || m_ActivityInfo.maxTeamSize == 1 && lobby.players.size() >= m_ActivityInfo.minTeams) {
|
||||
// Update the joining player on the match timer
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(playerLobby->timer);
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
LDFData<float> matchTimer("time", lobby.timer);
|
||||
GameMessages::SendMatchUpdate(player, player->GetSystemAddress(), matchTimer.GetString(), eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
|
||||
// Removes the player from a lobby and notifies the others, not applicable for non-lobby instances
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (int i = 0; i < lobby->players.size(); ++i) {
|
||||
if (lobby->players[i]->entityID == playerID) {
|
||||
std::string matchUpdateLeft = "player=9:" + std::to_string(playerID);
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
auto* entity = lobbyPlayer->GetEntity();
|
||||
for (auto& lobby : m_Queue | std::views::values) {
|
||||
for (int i = 0; i < lobby.players.size(); i++) {
|
||||
const auto& player = lobby.players[i];
|
||||
if (player.entityID == playerID) {
|
||||
LDFData<LWOOBJID> matchUpdateLeft("player", playerID);
|
||||
for (const auto& lobbyPlayer : lobby.players) {
|
||||
auto* const entity = lobbyPlayer.GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft, eMatchUpdate::PLAYER_REMOVED);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchUpdateLeft.GetString(), eMatchUpdate::PLAYER_REMOVED);
|
||||
}
|
||||
|
||||
delete lobby->players[i];
|
||||
lobby->players[i] = nullptr;
|
||||
lobby->players.erase(lobby->players.begin() + i);
|
||||
lobby.players.erase(lobby.players.begin() + i);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -191,85 +200,79 @@ void ActivityComponent::PlayerLeave(LWOOBJID playerID) {
|
||||
}
|
||||
|
||||
void ActivityComponent::Update(float deltaTime) {
|
||||
std::vector<Lobby*> lobbiesToRemove{};
|
||||
std::vector<LWOOBJID> lobbiesToRemove{};
|
||||
// Ticks all the lobbies, not applicable for non-instance activities
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
for (auto& [lobbyID, lobby] : m_Queue) {
|
||||
for (const auto& player : lobby.players) {
|
||||
const auto* const entity = player.GetEntity();
|
||||
if (entity == nullptr) {
|
||||
PlayerLeave(player->entityID);
|
||||
PlayerLeave(player.entityID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lobby->players.empty()) {
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
if (lobby.players.empty()) {
|
||||
lobbiesToRemove.push_back(lobbyID);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the match time for all players
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby->players.size() >= m_ActivityInfo.minTeamSize
|
||||
|| m_ActivityInfo.maxTeamSize == 1 && lobby->players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (lobby->timer == m_ActivityInfo.waitTime / 1000) {
|
||||
for (LobbyPlayer* joinedPlayer : lobby->players) {
|
||||
auto* entity = joinedPlayer->GetEntity();
|
||||
if (m_ActivityInfo.maxTeamSize != 1 && lobby.players.size() >= m_ActivityInfo.minTeamSize
|
||||
|| m_ActivityInfo.maxTeamSize == 1 && lobby.players.size() >= m_ActivityInfo.minTeams) {
|
||||
if (lobby.timer == m_ActivityInfo.waitTime / 1000) {
|
||||
for (const auto& joinedPlayer : lobby.players) {
|
||||
auto* const entity = joinedPlayer.GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_READY);
|
||||
LDFData<float> matchTimerUpdate("time", lobby.timer);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate.GetString(), eMatchUpdate::PHASE_WAIT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
lobby->timer -= deltaTime;
|
||||
lobby.timer -= deltaTime;
|
||||
}
|
||||
|
||||
bool lobbyReady = true;
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
if (player->ready) continue;
|
||||
for (const auto& player : lobby.players) {
|
||||
if (player.ready) continue;
|
||||
lobbyReady = false;
|
||||
}
|
||||
|
||||
// If everyone's ready, jump the timer
|
||||
if (lobbyReady && lobby->timer > m_ActivityInfo.startDelay / 1000) {
|
||||
lobby->timer = m_ActivityInfo.startDelay / 1000;
|
||||
if (lobbyReady && lobby.timer > m_ActivityInfo.startDelay / 1000) {
|
||||
lobby.timer = m_ActivityInfo.startDelay / 1000;
|
||||
|
||||
// Update players in lobby on switch to start delay
|
||||
std::string matchTimerUpdate = "time=3:" + std::to_string(lobby->timer);
|
||||
for (LobbyPlayer* player : lobby->players) {
|
||||
auto* entity = player->GetEntity();
|
||||
LDFData<float> matchTimerUpdate("time", lobby.timer);
|
||||
for (const auto& player : lobby.players) {
|
||||
auto* const entity = player.GetEntity();
|
||||
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate, eMatchUpdate::PHASE_WAIT_START);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchTimerUpdate.GetString(), eMatchUpdate::PHASE_WAIT_START);
|
||||
}
|
||||
}
|
||||
|
||||
// The timer has elapsed, start the instance
|
||||
if (lobby->timer <= 0.0f) {
|
||||
if (lobby.timer <= 0.0f) {
|
||||
LOG("Setting up instance.");
|
||||
ActivityInstance* instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby->players);
|
||||
instance->StartZone();
|
||||
lobbiesToRemove.push_back(lobby);
|
||||
auto& instance = NewInstance();
|
||||
LoadPlayersIntoInstance(instance, lobby.players);
|
||||
instance.StartZone();
|
||||
lobbiesToRemove.push_back(lobbyID);
|
||||
}
|
||||
}
|
||||
|
||||
while (!lobbiesToRemove.empty()) {
|
||||
RemoveLobby(lobbiesToRemove.front());
|
||||
lobbiesToRemove.erase(lobbiesToRemove.begin());
|
||||
for (const auto id : lobbiesToRemove) {
|
||||
RemoveLobby(id);
|
||||
}
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveLobby(Lobby* lobby) {
|
||||
for (int i = 0; i < m_Queue.size(); ++i) {
|
||||
if (m_Queue[i] == lobby) {
|
||||
m_Queue.erase(m_Queue.begin() + i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void ActivityComponent::RemoveLobby(const LWOOBJID lobbyID) {
|
||||
if (m_Queue.contains(lobbyID)) m_Queue.erase(lobbyID);
|
||||
}
|
||||
|
||||
bool ActivityComponent::HasLobby() const {
|
||||
@@ -278,9 +281,9 @@ bool ActivityComponent::HasLobby() const {
|
||||
}
|
||||
|
||||
bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (player->GetObjectID() == lobbyPlayer->entityID) return true;
|
||||
for (const auto& lobby : m_Queue | std::views::values) {
|
||||
for (const auto& lobbyPlayer : lobby.players) {
|
||||
if (player->GetObjectID() == lobbyPlayer.entityID) return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,8 +291,8 @@ bool ActivityComponent::PlayerIsInQueue(Entity* player) {
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
for (const auto& instance : m_Instances) {
|
||||
for (const auto* instancePlayer : instance.GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID())
|
||||
return true;
|
||||
}
|
||||
@@ -299,8 +302,8 @@ bool ActivityComponent::IsPlayedBy(Entity* player) const {
|
||||
}
|
||||
|
||||
bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const {
|
||||
for (const auto* instance : this->m_Instances) {
|
||||
for (const auto* instancePlayer : instance->GetParticipants()) {
|
||||
for (const auto& instance : m_Instances) {
|
||||
for (const auto* instancePlayer : instance.GetParticipants()) {
|
||||
if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID)
|
||||
return true;
|
||||
}
|
||||
@@ -329,136 +332,95 @@ bool ActivityComponent::TakeCost(Entity* player) const {
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerReady(Entity* player, bool bReady) {
|
||||
for (Lobby* lobby : m_Queue) {
|
||||
for (LobbyPlayer* lobbyPlayer : lobby->players) {
|
||||
if (lobbyPlayer->entityID == player->GetObjectID()) {
|
||||
for (auto& lobby : m_Queue | std::views::values) {
|
||||
for (auto& lobbyPlayer : lobby.players) {
|
||||
if (lobbyPlayer.entityID == player->GetObjectID()) {
|
||||
|
||||
lobbyPlayer->ready = bReady;
|
||||
lobbyPlayer.ready = bReady;
|
||||
|
||||
// Update players in lobby on player being ready
|
||||
std::string matchReadyUpdate = "player=9:" + std::to_string(player->GetObjectID());
|
||||
LDFData<LWOOBJID> matchReadyUpdate("player", player->GetObjectID());
|
||||
eMatchUpdate readyStatus = eMatchUpdate::PLAYER_READY;
|
||||
if (!bReady) readyStatus = eMatchUpdate::PLAYER_NOT_READY;
|
||||
for (LobbyPlayer* otherPlayer : lobby->players) {
|
||||
auto* entity = otherPlayer->GetEntity();
|
||||
for (const auto& otherPlayer : lobby.players) {
|
||||
auto* const entity = otherPlayer.GetEntity();
|
||||
if (entity == nullptr)
|
||||
continue;
|
||||
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate, readyStatus);
|
||||
GameMessages::SendMatchUpdate(entity, entity->GetSystemAddress(), matchReadyUpdate.GetString(), readyStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::NewInstance() {
|
||||
auto* instance = new ActivityInstance(m_Parent, m_ActivityInfo);
|
||||
m_Instances.push_back(instance);
|
||||
return instance;
|
||||
ActivityInstance& ActivityComponent::NewInstance() {
|
||||
m_Instances.push_back(ActivityInstance(m_Parent, m_ActivityInfo));
|
||||
return m_Instances.back();
|
||||
}
|
||||
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const {
|
||||
for (LobbyPlayer* player : lobby) {
|
||||
auto* entity = player->GetEntity();
|
||||
void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance& instance, const std::vector<LobbyPlayer>& lobby) const {
|
||||
for (const auto& player : lobby) {
|
||||
auto* const entity = player.GetEntity();
|
||||
if (entity == nullptr || !CheckCost(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
instance->AddParticipant(entity);
|
||||
instance.AddParticipant(entity);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<ActivityInstance*>& ActivityComponent::GetInstances() const {
|
||||
return m_Instances;
|
||||
}
|
||||
|
||||
ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) {
|
||||
for (const auto* instance : GetInstances()) {
|
||||
for (const auto* participant : instance->GetParticipants()) {
|
||||
const ActivityInstance& ActivityComponent::GetInstance(const LWOOBJID playerID) const {
|
||||
for (const auto& instance : m_Instances) {
|
||||
for (const auto* participant : instance.GetParticipants()) {
|
||||
if (participant->GetObjectID() == playerID)
|
||||
return const_cast<ActivityInstance*>(instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return g_EmptyInstance;
|
||||
}
|
||||
|
||||
void ActivityComponent::ClearInstances() {
|
||||
for (ActivityInstance* instance : m_Instances) {
|
||||
delete instance;
|
||||
}
|
||||
m_Instances.clear();
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) {
|
||||
for (auto* activityData : m_ActivityPlayers) {
|
||||
if (activityData->playerID == playerID) {
|
||||
return activityData;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
bool ActivityComponent::PlayerHasActivityData(LWOOBJID playerID) const {
|
||||
return m_ActivityPlayers.contains(playerID);
|
||||
}
|
||||
|
||||
void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) {
|
||||
for (size_t i = 0; i < m_ActivityPlayers.size(); i++) {
|
||||
if (m_ActivityPlayers[i]->playerID == playerID) {
|
||||
delete m_ActivityPlayers[i];
|
||||
m_ActivityPlayers[i] = nullptr;
|
||||
|
||||
m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i);
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) {
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} });
|
||||
m_ActivityPlayers.erase(playerID);
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
|
||||
return GetActivityPlayerData(playerID);
|
||||
}
|
||||
|
||||
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) {
|
||||
auto value = -1.0f;
|
||||
float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) const {
|
||||
float value = -1.0f;
|
||||
|
||||
auto* data = GetActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
value = data->values[std::min(index, static_cast<uint32_t>(9))];
|
||||
const auto& data = m_ActivityPlayers.find(playerID);
|
||||
if (data != m_ActivityPlayers.cend()) {
|
||||
value = data->second[std::min(index, static_cast<uint32_t>(9))];
|
||||
}
|
||||
|
||||
LOG_DEBUG("Player %llu has score %f at index %i", playerID, value, index);
|
||||
return value;
|
||||
}
|
||||
|
||||
void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) {
|
||||
auto* data = AddActivityPlayerData(playerID);
|
||||
if (data != nullptr) {
|
||||
data->values[std::min(index, static_cast<uint32_t>(9))] = value;
|
||||
}
|
||||
auto& data = m_ActivityPlayers[playerID];
|
||||
data[std::min(index, static_cast<uint32_t>(9))] = value;
|
||||
LOG_DEBUG("%llu index %i has score of %f", playerID, index, value);
|
||||
m_DirtyActivityInfo = true;
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void ActivityComponent::PlayerRemove(LWOOBJID playerID) {
|
||||
for (auto* instance : GetInstances()) {
|
||||
auto participants = instance->GetParticipants();
|
||||
for (int i = 0; i < m_Instances.size(); i++) {
|
||||
auto& instance = m_Instances[i];
|
||||
auto participants = instance.GetParticipants();
|
||||
for (const auto* participant : participants) {
|
||||
if (participant != nullptr && participant->GetObjectID() == playerID) {
|
||||
instance->RemoveParticipant(participant);
|
||||
instance.RemoveParticipant(participant);
|
||||
RemoveActivityPlayerData(playerID);
|
||||
|
||||
// If the instance is empty after the delete of the participant, delete the instance too
|
||||
if (instance->GetParticipants().empty()) {
|
||||
m_Instances.erase(std::find(m_Instances.begin(), m_Instances.end(), instance));
|
||||
delete instance;
|
||||
if (instance.GetParticipants().empty()) {
|
||||
m_Instances.erase(m_Instances.begin() + i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -595,14 +557,13 @@ bool ActivityComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo&
|
||||
auto& instances = activityInfo.PushDebug("Instances: " + std::to_string(m_Instances.size()));
|
||||
size_t i = 0;
|
||||
for (const auto& activityInstance : m_Instances) {
|
||||
if (!activityInstance) continue;
|
||||
auto& instance = instances.PushDebug("Instance " + std::to_string(i++));
|
||||
instance.PushDebug<AMFIntValue>("Score") = activityInstance->GetScore();
|
||||
instance.PushDebug<AMFIntValue>("Next Zone Clone ID") = activityInstance->GetNextZoneCloneID();
|
||||
instance.PushDebug<AMFIntValue>("Score") = activityInstance.GetScore();
|
||||
instance.PushDebug<AMFIntValue>("Next Zone Clone ID") = activityInstance.GetNextZoneCloneID();
|
||||
|
||||
{
|
||||
auto& activityInfo = instance.PushDebug("Activity Info");
|
||||
const auto& instanceActInfo = activityInstance->GetActivityInfo();
|
||||
const auto& instanceActInfo = activityInstance.GetActivityInfo();
|
||||
activityInfo.PushDebug<AMFIntValue>("ActivityID") = instanceActInfo.ActivityID;
|
||||
activityInfo.PushDebug<AMFIntValue>("locStatus") = instanceActInfo.locStatus;
|
||||
activityInfo.PushDebug<AMFIntValue>("instanceMapID") = instanceActInfo.instanceMapID;
|
||||
@@ -625,7 +586,7 @@ bool ActivityComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo&
|
||||
}
|
||||
|
||||
auto& participants = instance.PushDebug("Participants");
|
||||
for (const auto* participant : activityInstance->GetParticipants()) {
|
||||
for (const auto* participant : activityInstance.GetParticipants()) {
|
||||
if (!participant) continue;
|
||||
auto* character = participant->GetCharacter();
|
||||
if (!character) continue;
|
||||
@@ -635,38 +596,36 @@ bool ActivityComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo&
|
||||
|
||||
auto& queue = activityInfo.PushDebug("Queue");
|
||||
i = 0;
|
||||
for (const auto& lobbyQueue : m_Queue) {
|
||||
for (const auto& lobbyQueue : m_Queue | std::views::values) {
|
||||
auto& lobby = queue.PushDebug("Lobby " + std::to_string(i++));
|
||||
lobby.PushDebug<AMFDoubleValue>("Timer") = lobbyQueue->timer;
|
||||
lobby.PushDebug<AMFDoubleValue>("Timer") = lobbyQueue.timer;
|
||||
|
||||
auto& players = lobby.PushDebug("Players");
|
||||
for (const auto* player : lobbyQueue->players) {
|
||||
if (!player) continue;
|
||||
auto* playerEntity = player->GetEntity();
|
||||
for (const auto& player : lobbyQueue.players) {
|
||||
const auto* const playerEntity = player.GetEntity();
|
||||
if (!playerEntity) continue;
|
||||
auto* character = playerEntity->GetCharacter();
|
||||
if (!character) continue;
|
||||
|
||||
players.PushDebug<AMFStringValue>(std::to_string(playerEntity->GetObjectID()) + ": " + character->GetName()) = player->ready ? "Ready" : "Not Ready";
|
||||
players.PushDebug<AMFStringValue>(std::to_string(playerEntity->GetObjectID()) + ": " + character->GetName()) = player.ready ? "Ready" : "Not Ready";
|
||||
}
|
||||
}
|
||||
|
||||
auto& activityPlayers = activityInfo.PushDebug("Activity Players");
|
||||
for (const auto* activityPlayer : m_ActivityPlayers) {
|
||||
if (!activityPlayer) continue;
|
||||
auto* const activityPlayerEntity = Game::entityManager->GetEntity(activityPlayer->playerID);
|
||||
for (const auto& [playerID, playerScores] : m_ActivityPlayers) {
|
||||
auto* const activityPlayerEntity = Game::entityManager->GetEntity(playerID);
|
||||
if (!activityPlayerEntity) continue;
|
||||
auto* character = activityPlayerEntity->GetCharacter();
|
||||
if (!character) continue;
|
||||
|
||||
auto& playerData = activityPlayers.PushDebug(std::to_string(activityPlayer->playerID) + " " + character->GetName());
|
||||
auto& playerData = activityPlayers.PushDebug(std::to_string(playerID) + " " + character->GetName());
|
||||
|
||||
auto& scores = playerData.PushDebug("Scores");
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
scores.PushDebug<AMFDoubleValue>(std::to_string(i)) = activityPlayer->values[i];
|
||||
scores.PushDebug<AMFDoubleValue>(std::to_string(i)) = playerScores[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
activityInfo.PushDebug<AMFIntValue>("ActivityID") = m_ActivityID;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
#include "CDActivitiesTable.h"
|
||||
#include <array>
|
||||
|
||||
namespace GameMessages {
|
||||
class GameMsg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an instance of an activity, having participants and score
|
||||
*/
|
||||
/**
|
||||
* Represents an instance of an activity, having participants and score
|
||||
*/
|
||||
class ActivityInstance {
|
||||
public:
|
||||
ActivityInstance(Entity* parent, CDActivities activityInfo) { m_Parent = parent; m_ActivityInfo = activityInfo; };
|
||||
@@ -104,7 +105,7 @@ struct LobbyPlayer {
|
||||
/**
|
||||
* The ID of the entity that is in the lobby
|
||||
*/
|
||||
LWOOBJID entityID;
|
||||
LWOOBJID entityID = LWOOBJID_EMPTY;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is ready
|
||||
@@ -126,12 +127,12 @@ struct Lobby {
|
||||
/**
|
||||
* The lobby of players
|
||||
*/
|
||||
std::vector<LobbyPlayer*> players;
|
||||
std::vector<LobbyPlayer> players;
|
||||
|
||||
/**
|
||||
* The timer that determines when the activity should start
|
||||
*/
|
||||
float timer;
|
||||
float timer{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -142,12 +143,12 @@ struct ActivityPlayer {
|
||||
/**
|
||||
* The entity that the score is tracked for
|
||||
*/
|
||||
LWOOBJID playerID;
|
||||
LWOOBJID playerID{};
|
||||
|
||||
/**
|
||||
* The list of score for this entity
|
||||
*/
|
||||
float values[10];
|
||||
float values[10]{};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -194,13 +195,13 @@ public:
|
||||
* @param instance the instance to load the players into
|
||||
* @param lobby the players to load into the instance
|
||||
*/
|
||||
void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector<LobbyPlayer*>& lobby) const;
|
||||
void LoadPlayersIntoInstance(ActivityInstance& instance, const std::vector<LobbyPlayer>& lobby) const;
|
||||
|
||||
/**
|
||||
* Removes a lobby from the activity manager
|
||||
* @param lobby the lobby to remove
|
||||
*/
|
||||
void RemoveLobby(Lobby* lobby);
|
||||
void RemoveLobby(const LWOOBJID lobbyID);
|
||||
|
||||
/**
|
||||
* Marks a player as (un)ready in a lobby
|
||||
@@ -246,7 +247,7 @@ public:
|
||||
*/
|
||||
bool IsPlayedBy(LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Checks if the entity has enough cost to play this activity
|
||||
* @param player the entity to check
|
||||
* @return true if the entity has enough cost to play this activity, false otherwise
|
||||
@@ -271,20 +272,14 @@ public:
|
||||
* Creates a new instance for this activity
|
||||
* @return a new instance for this activity
|
||||
*/
|
||||
ActivityInstance* NewInstance();
|
||||
|
||||
/**
|
||||
* Returns all the currently active instances of this activity
|
||||
* @return all the currently active instances of this activity
|
||||
*/
|
||||
const std::vector<ActivityInstance*>& GetInstances() const;
|
||||
ActivityInstance& NewInstance();
|
||||
|
||||
/**
|
||||
* Returns the instance that some entity is currently playing in
|
||||
* @param playerID the entity to check for
|
||||
* @return if any, the instance that the entity is currently in
|
||||
*/
|
||||
ActivityInstance* GetInstance(const LWOOBJID playerID);
|
||||
const ActivityInstance& GetInstance(const LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
* @brief Reloads the config settings for this component
|
||||
@@ -292,23 +287,12 @@ public:
|
||||
*/
|
||||
void ReloadConfig();
|
||||
|
||||
/**
|
||||
* Removes all the instances
|
||||
*/
|
||||
void ClearInstances();
|
||||
|
||||
/**
|
||||
* Returns all the score for the players that are currently playing this activity
|
||||
* @return
|
||||
*/
|
||||
std::vector<ActivityPlayer*> GetActivityPlayers() { return m_ActivityPlayers; };
|
||||
|
||||
/**
|
||||
* Returns activity data for a specific entity (e.g. score and such).
|
||||
* @param playerID the entity to get data for
|
||||
* @return the activity data (score) for the passed player in this activity, if it exists
|
||||
*/
|
||||
ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID);
|
||||
bool PlayerHasActivityData(LWOOBJID playerID) const;
|
||||
|
||||
/**
|
||||
* Sets some score value for an entity
|
||||
@@ -324,7 +308,7 @@ public:
|
||||
* @param index the index to get score for
|
||||
* @return activity score for the passed parameters
|
||||
*/
|
||||
float_t GetActivityValue(LWOOBJID playerID, uint32_t index);
|
||||
float_t GetActivityValue(LWOOBJID playerID, uint32_t index) const;
|
||||
|
||||
/**
|
||||
* Removes activity score tracking for some entity
|
||||
@@ -332,13 +316,6 @@ public:
|
||||
*/
|
||||
void RemoveActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Adds activity score tracking for some entity
|
||||
* @param playerID the entity to add the activity score for
|
||||
* @return the created entry
|
||||
*/
|
||||
ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID);
|
||||
|
||||
/**
|
||||
* Sets the mapID that this activity points to
|
||||
* @param mapID the map ID to set
|
||||
@@ -346,7 +323,6 @@ public:
|
||||
void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; };
|
||||
|
||||
private:
|
||||
|
||||
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& msg);
|
||||
/**
|
||||
* The database information for this activity
|
||||
@@ -356,17 +332,17 @@ private:
|
||||
/**
|
||||
* All the active instances of this activity
|
||||
*/
|
||||
std::vector<ActivityInstance*> m_Instances;
|
||||
std::vector<ActivityInstance> m_Instances;
|
||||
|
||||
/**
|
||||
* The current lobbies for this activity
|
||||
*/
|
||||
std::vector<Lobby*> m_Queue;
|
||||
std::map<LWOOBJID, Lobby> m_Queue;
|
||||
|
||||
/**
|
||||
* All the activity score for the players in this activity
|
||||
*/
|
||||
std::vector<ActivityPlayer*> m_ActivityPlayers;
|
||||
std::map<LWOOBJID, std::array<float, 10>> m_ActivityPlayers;
|
||||
|
||||
/**
|
||||
* The activity id
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "SkillComponent.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "dNavMesh.h"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "SimplePhysicsComponent.h"
|
||||
#include "Amf3.h"
|
||||
#include "dpShapeSphere.h"
|
||||
|
||||
const std::unordered_set<LWOOBJID> ProximityMonitorComponent::m_EmptyObjectSet = {};
|
||||
|
||||
@@ -12,6 +14,7 @@ ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, const int32
|
||||
SetProximityRadius(radiusSmall, "rocketSmall");
|
||||
SetProximityRadius(radiusLarge, "rocketLarge");
|
||||
}
|
||||
RegisterMsg(&ProximityMonitorComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
ProximityMonitorComponent::~ProximityMonitorComponent() {
|
||||
@@ -60,6 +63,31 @@ bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID
|
||||
return collisions.contains(objectID);
|
||||
}
|
||||
|
||||
bool ProximityMonitorComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo) {
|
||||
auto& proxInfo = reportInfo.info->PushDebug("Proximity Monitor");
|
||||
for (const auto& [name, entity] : m_ProximitiesData) {
|
||||
if (!entity) continue;
|
||||
auto& proxAmf = proxInfo.PushDebug(name);
|
||||
const auto* const shape = entity->GetShape();
|
||||
if (shape && shape->GetShapeType() == dpShapeType::Sphere) {
|
||||
const auto* const sphere = static_cast<const dpShapeSphere*>(shape);
|
||||
proxAmf.PushDebug<AMFDoubleValue>("Radius") = sphere->GetRadius();
|
||||
}
|
||||
proxAmf.PushDebug<AMFBoolValue>("Sleeping") = entity->GetSleeping();
|
||||
proxAmf.PushDebug<AMFDoubleValue>("Scale") = entity->GetScale();
|
||||
proxAmf.PushDebug<AMFBoolValue>("Gargantuan") = entity->GetIsGargantuan();
|
||||
proxAmf.PushDebug<AMFBoolValue>("Static") = entity->GetIsStatic();
|
||||
proxAmf.PushDebug("Position").PushDebug(entity->GetPosition());
|
||||
proxAmf.PushDebug("Rotation").PushDebug(entity->GetRotation());
|
||||
auto& collidingAmf = proxAmf.PushDebug("Colliding Objects");
|
||||
for (const auto& colliding : entity->GetCurrentlyCollidingObjects()) {
|
||||
collidingAmf.PushDebug(std::to_string(colliding));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProximityMonitorComponent::Update(float deltaTime) {
|
||||
for (const auto& prox : m_ProximitiesData) {
|
||||
if (!prox.second) continue;
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
|
||||
/**
|
||||
* All the proximity sensors for this component, indexed by name
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "RenderComponent.h"
|
||||
|
||||
#include "CppScripts.h"
|
||||
#include "StringifiedEnum.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
QuickBuildComponent::QuickBuildComponent(Entity* const entity, const int32_t componentID) : Component{ entity, componentID } {
|
||||
std::u16string checkPreconditions = entity->GetVar<std::u16string>(u"CheckPrecondition");
|
||||
@@ -42,6 +44,7 @@ QuickBuildComponent::QuickBuildComponent(Entity* const entity, const int32_t com
|
||||
}
|
||||
|
||||
SpawnActivator();
|
||||
RegisterMsg(&QuickBuildComponent::OnGetObjectReportInfo);
|
||||
}
|
||||
|
||||
QuickBuildComponent::~QuickBuildComponent() {
|
||||
@@ -568,3 +571,30 @@ void QuickBuildComponent::AddQuickBuildCompleteCallback(const std::function<void
|
||||
void QuickBuildComponent::AddQuickBuildStateCallback(const std::function<void(eQuickBuildState state)>& callback) {
|
||||
m_QuickBuildStateCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
bool QuickBuildComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo) {
|
||||
auto& quickbuild = reportInfo.info->PushDebug("Quick Build");
|
||||
quickbuild.PushDebug<AMFStringValue>("State") = StringifiedEnum::ToString(m_State).data();
|
||||
quickbuild.PushDebug<AMFDoubleValue>("Timer") = m_Timer;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("TimerIncomplete") = m_TimerIncomplete;
|
||||
quickbuild.PushDebug("ActivatorPosition").PushDebug(m_ActivatorPosition);
|
||||
quickbuild.PushDebug<AMFStringValue>("ActivatorId") = std::to_string(m_ActivatorId);
|
||||
quickbuild.PushDebug<AMFBoolValue>("ShowResetEffect") = m_ShowResetEffect;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("Taken") = m_Taken;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("ResetTime") = m_ResetTime;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("CompleteTime") = m_CompleteTime;
|
||||
quickbuild.PushDebug<AMFIntValue>("TakeImagination") = m_TakeImagination;
|
||||
quickbuild.PushDebug<AMFBoolValue>("Interruptible") = m_Interruptible;
|
||||
quickbuild.PushDebug<AMFBoolValue>("SelfActivator") = m_SelfActivator;
|
||||
auto& modules = quickbuild.PushDebug("CustomModules");
|
||||
for (const auto cmodule : m_CustomModules) modules.PushDebug<AMFIntValue>("Module") = cmodule;
|
||||
quickbuild.PushDebug<AMFIntValue>("ActivityId") = m_ActivityId;
|
||||
quickbuild.PushDebug<AMFIntValue>("PostImaginationCost") = m_PostImaginationCost;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("TimeBeforeSmash") = m_TimeBeforeSmash;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("TimeBeforeDrain") = m_TimeBeforeDrain;
|
||||
quickbuild.PushDebug<AMFIntValue>("DrainedImagination") = m_DrainedImagination;
|
||||
quickbuild.PushDebug<AMFBoolValue>("RepositionPlayer") = m_RepositionPlayer;
|
||||
quickbuild.PushDebug<AMFDoubleValue>("SoftTimer") = m_SoftTimer;
|
||||
quickbuild.PushDebug<AMFStringValue>("Builder") = std::to_string(m_Builder);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,6 +261,8 @@ public:
|
||||
m_StateDirty = true;
|
||||
}
|
||||
private:
|
||||
|
||||
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
|
||||
/**
|
||||
* Whether or not the quickbuild state has been changed since we last serialized it.
|
||||
*/
|
||||
|
||||
@@ -3221,7 +3221,7 @@ void GameMessages::HandleClientTradeRequest(RakNet::BitStream& inStream, Entity*
|
||||
|
||||
LOG("Trade request to (%llu)", i64Invitee);
|
||||
|
||||
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
const auto& trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
|
||||
if (trade != nullptr) {
|
||||
if (!trade->IsParticipant(i64Invitee)) {
|
||||
@@ -3244,7 +3244,7 @@ void GameMessages::HandleClientTradeRequest(RakNet::BitStream& inStream, Entity*
|
||||
}
|
||||
|
||||
void GameMessages::HandleClientTradeCancel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
const auto& trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
|
||||
if (trade == nullptr) return;
|
||||
|
||||
@@ -3258,7 +3258,7 @@ void GameMessages::HandleClientTradeAccept(RakNet::BitStream& inStream, Entity*
|
||||
|
||||
LOG("Trade accepted from (%llu) -> (%d)", entity->GetObjectID(), bFirst);
|
||||
|
||||
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
const auto& trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
|
||||
if (trade == nullptr) return;
|
||||
|
||||
@@ -3324,7 +3324,7 @@ void GameMessages::HandleClientTradeUpdate(RakNet::BitStream& inStream, Entity*
|
||||
LOG("Trade item from (%llu) -> (%llu)/(%llu), (%i), (%llu), (%i), (%i)", entity->GetObjectID(), itemId, itemId2, lot, unknown1, unknown2, unknown3);
|
||||
}
|
||||
|
||||
auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
const auto& trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID());
|
||||
|
||||
if (trade == nullptr) return;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "eMissionState.h"
|
||||
#include "PetComponent.h"
|
||||
|
||||
std::map<uint32_t, Precondition*> Preconditions::cache = {};
|
||||
|
||||
@@ -79,6 +80,9 @@ bool Precondition::Check(Entity* player, bool evaluateCosts) const {
|
||||
case PreconditionType::DoesNotHaveRacingLicence:
|
||||
case PreconditionType::LegoClubMember:
|
||||
case PreconditionType::NoInteraction:
|
||||
case PreconditionType::NotFreeTrial:
|
||||
case PreconditionType::MissionActive:
|
||||
case PreconditionType::DoesNotHaveFlag:
|
||||
any = true;
|
||||
break;
|
||||
case PreconditionType::DoesNotHaveItem:
|
||||
@@ -154,7 +158,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||
case PreconditionType::PetDeployed:
|
||||
return false; // TODO
|
||||
return PetComponent::GetActivePet(player->GetObjectID()) != nullptr;
|
||||
case PreconditionType::HasFlag:
|
||||
return character->GetPlayerFlag(value);
|
||||
case PreconditionType::WithinShape:
|
||||
@@ -162,9 +166,9 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
||||
case PreconditionType::InBuild:
|
||||
return character->GetBuildMode();
|
||||
case PreconditionType::TeamCheck:
|
||||
return false; // TODO
|
||||
return false; // TODO: requires knowing the player's minigame team assignment (red/blue etc.); DLU does not track this per-player
|
||||
case PreconditionType::IsPetTaming:
|
||||
return false; // TODO
|
||||
return PetComponent::GetTamingPet(player->GetObjectID()) != nullptr;
|
||||
case PreconditionType::HasFaction:
|
||||
for (const auto faction : destroyableComponent->GetFactionIDs()) {
|
||||
if (faction == static_cast<int>(value)) {
|
||||
@@ -182,15 +186,24 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
||||
|
||||
return true;
|
||||
case PreconditionType::HasRacingLicence:
|
||||
return false; // TODO
|
||||
return false; // TODO: requires a racing licence level on the player; DLU does not track this
|
||||
case PreconditionType::DoesNotHaveRacingLicence:
|
||||
return false; // TODO
|
||||
return false; // TODO: requires a racing licence level on the player; DLU does not track this
|
||||
case PreconditionType::LegoClubMember:
|
||||
return false; // TODO
|
||||
return true; // Live LU opened LEGO CLUB to All players at some point, so always return true
|
||||
case PreconditionType::NoInteraction:
|
||||
return false; // TODO
|
||||
return false; // TODO: requires tracking the player's currently active interaction object; DLU does not track this
|
||||
case PreconditionType::HasLevel:
|
||||
return levelComponent->GetLevel() >= value;
|
||||
case PreconditionType::NotFreeTrial:
|
||||
return true; // DLU does not support free trial accounts; all players pass this check
|
||||
case PreconditionType::MissionActive: {
|
||||
if (missionComponent == nullptr) return false;
|
||||
const auto state = missionComponent->GetMissionState(value);
|
||||
return state == eMissionState::ACTIVE || state == eMissionState::COMPLETE_ACTIVE;
|
||||
}
|
||||
case PreconditionType::DoesNotHaveFlag:
|
||||
return !character->GetPlayerFlag(value);
|
||||
default:
|
||||
return true; // There are a couple more unknown preconditions. Always return true in this case.
|
||||
}
|
||||
@@ -230,6 +243,7 @@ PreconditionExpression::PreconditionExpression(const std::string& conditions) {
|
||||
case '&':
|
||||
case ';':
|
||||
case '(':
|
||||
case ':':
|
||||
b << conditions.substr(i + 1);
|
||||
done = true;
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,10 @@ enum class PreconditionType
|
||||
DoesNotHaveRacingLicence,
|
||||
LegoClubMember,
|
||||
NoInteraction,
|
||||
HasLevel = 22
|
||||
NotFreeTrial,
|
||||
MissionActive,
|
||||
HasLevel,
|
||||
DoesNotHaveFlag = 23
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "dpShapeSphere.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "Metrics.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "SlashCommandHandler.h"
|
||||
#include "UserManager.h"
|
||||
@@ -1288,18 +1288,14 @@ namespace DEVGMCommands {
|
||||
response.Insert("serverInfo", true);
|
||||
auto* info = response.InsertArray("data");
|
||||
for (const auto variable : Metrics::GetAllMetrics()) {
|
||||
auto& metricData = info->PushDebug(StringifiedEnum::ToString(variable));
|
||||
auto& metricData = info->PushDebug(Metrics::MetricVariableToString(variable));
|
||||
|
||||
auto* metric = Metrics::GetMetric(variable);
|
||||
const auto& metric = Metrics::GetMetric(variable);
|
||||
|
||||
if (metric == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
metricData.PushDebug<AMFStringValue>("Maximum") = std::to_string(Metrics::ToMiliseconds(metric->max)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Minimum") = std::to_string(Metrics::ToMiliseconds(metric->min)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Average") = std::to_string(Metrics::ToMiliseconds(metric->average)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Measurements Count") = std::to_string(metric->measurementSize);
|
||||
metricData.PushDebug<AMFStringValue>("Maximum") = std::to_string(Metrics::ToMiliseconds(metric.max)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Minimum") = std::to_string(Metrics::ToMiliseconds(metric.min)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Average") = std::to_string(Metrics::ToMiliseconds(metric.average)) + "ms";
|
||||
metricData.PushDebug<AMFStringValue>("Measurements Count") = std::to_string(metric.measurementSize);
|
||||
}
|
||||
auto& processInfo = info->PushDebug("Process Info");
|
||||
processInfo.PushDebug<AMFStringValue>("Peak RSS") = std::to_string(static_cast<double>(Metrics::GetPeakRSS()) / 1.024e6) + "MB";
|
||||
|
||||
@@ -332,6 +332,12 @@ int InstanceManager::GetHardCap(LWOMAPID mapID) {
|
||||
return zone ? zone->population_hard_cap : 12;
|
||||
}
|
||||
|
||||
void InstanceManager::PruneUnreadyInstances() {
|
||||
for (int i = static_cast<int>(m_Instances.size()) - 1; i >= 0; i--) {
|
||||
if (!m_Instances[i]->GetIsReady()) m_Instances.erase(m_Instances.cbegin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
void Instance::SetShutdownComplete(const bool value) {
|
||||
m_Shutdown = value;
|
||||
}
|
||||
@@ -359,4 +365,3 @@ bool Instance::IsFull(bool isFriendTransfer) const {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ public:
|
||||
const InstancePtr& CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID cloneID, const std::string& password);
|
||||
const InstancePtr& FindPrivateInstance(const std::string& password);
|
||||
void SetIsShuttingDown(bool value) { this->m_IsShuttingDown = value; };
|
||||
void PruneUnreadyInstances();
|
||||
|
||||
private:
|
||||
std::string mExternalIP;
|
||||
|
||||
@@ -665,7 +665,7 @@ void HandlePacket(Packet* packet) {
|
||||
inStream.Read(theirInstanceID);
|
||||
|
||||
const auto& instance =
|
||||
Game::im->FindInstance(theirZoneID, theirInstanceID);
|
||||
Game::im->FindInstanceWithPrivate(theirZoneID, theirInstanceID);
|
||||
if (instance) {
|
||||
instance->AddPlayer(Player());
|
||||
} else {
|
||||
@@ -762,7 +762,7 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
LOG("Got world ready %i %i", zoneID, instanceID);
|
||||
|
||||
const auto& instance = Game::im->FindInstance(zoneID, instanceID);
|
||||
const auto& instance = Game::im->FindInstanceWithPrivate(zoneID, instanceID);
|
||||
|
||||
if (instance == nullptr) {
|
||||
LOG("Failed to find zone to ready");
|
||||
@@ -858,13 +858,11 @@ int ShutdownSequence(int32_t signal) {
|
||||
}
|
||||
|
||||
// A server might not be finished spinning up yet, remove all of those here.
|
||||
// prune the unready ones before looping over all of them
|
||||
Game::im->PruneUnreadyInstances();
|
||||
for (const auto& instance : Game::im->GetInstances()) {
|
||||
if (!instance) continue;
|
||||
|
||||
if (!instance->GetIsReady()) {
|
||||
Game::im->RemoveInstance(instance);
|
||||
}
|
||||
|
||||
instance->SetIsShuttingDown(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
#define _VARIADIC_MAX 10
|
||||
#include "ZoneInstanceManager.h"
|
||||
|
||||
// Custom Classes
|
||||
#include "MasterPackets.h"
|
||||
#include "dServer.h"
|
||||
|
||||
// C++
|
||||
#include <future>
|
||||
|
||||
// Static Variables
|
||||
ZoneInstanceManager* ZoneInstanceManager::m_Address = nullptr;
|
||||
|
||||
//! Requests a zone transfer
|
||||
void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID, uint32_t zoneClone, bool mythranShift, std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)> callback) {
|
||||
void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID, uint32_t zoneClone, bool mythranShift, TransferCallback callback) {
|
||||
const auto nextID = ++currentRequestID;
|
||||
requests[nextID] = callback;
|
||||
|
||||
ZoneTransferRequest* request = new ZoneTransferRequest();
|
||||
request->requestID = ++currentRequestID;
|
||||
request->callback = callback;
|
||||
|
||||
this->requests.push_back(request);
|
||||
|
||||
MasterPackets::SendZoneTransferRequest(server, request->requestID, mythranShift, zoneID, zoneClone);
|
||||
MasterPackets::SendZoneTransferRequest(server, nextID, mythranShift, zoneID, zoneClone);
|
||||
}
|
||||
|
||||
//! Handles a zone transfer response
|
||||
@@ -43,18 +34,11 @@ void ZoneInstanceManager::HandleRequestZoneTransferResponse(Packet* packet) {
|
||||
LUString serverIP(255);
|
||||
inStream.Read(serverIP);
|
||||
|
||||
for (uint32_t i = 0; i < this->requests.size(); ++i) {
|
||||
if (this->requests[i]->requestID == requestID) {
|
||||
|
||||
// Call the request callback
|
||||
this->requests[i]->callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP.string, serverPort);
|
||||
|
||||
delete this->requests[i];
|
||||
this->requests.erase(this->requests.begin() + i);
|
||||
return;
|
||||
}
|
||||
const auto entry = requests.find(requestID);
|
||||
if (entry != requests.end()) {
|
||||
entry->second(mythranShift, zoneID, zoneInstance, zoneClone, serverIP.string, serverPort);
|
||||
requests.erase(entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ZoneInstanceManager::CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password) {
|
||||
@@ -65,12 +49,9 @@ void ZoneInstanceManager::RequestPrivateZone(
|
||||
dServer* server,
|
||||
bool mythranShift,
|
||||
const std::string& password,
|
||||
std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)> callback) {
|
||||
ZoneTransferRequest* request = new ZoneTransferRequest();
|
||||
request->requestID = ++currentRequestID;
|
||||
request->callback = callback;
|
||||
TransferCallback callback) {
|
||||
const auto nextID = ++currentRequestID;
|
||||
requests[nextID] = callback;
|
||||
|
||||
this->requests.push_back(request);
|
||||
|
||||
MasterPackets::SendZoneRequestPrivate(server, request->requestID, mythranShift, password);
|
||||
MasterPackets::SendZoneRequestPrivate(server, nextID, mythranShift, password);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@@ -14,25 +15,20 @@ class dServer;
|
||||
\brief A class for handling zone transfers and zone-related functions
|
||||
*/
|
||||
|
||||
//! The zone request
|
||||
struct ZoneTransferRequest {
|
||||
uint64_t requestID;
|
||||
std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)> callback;
|
||||
};
|
||||
|
||||
//! The zone manager
|
||||
class ZoneInstanceManager {
|
||||
private:
|
||||
static ZoneInstanceManager* m_Address; //!< The singleton instance
|
||||
|
||||
std::vector<ZoneTransferRequest*> requests; //!< The zone transfer requests
|
||||
using TransferCallback = std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)>;
|
||||
std::map<uint64_t, TransferCallback> requests; //!< The zone transfer requests
|
||||
uint64_t currentRequestID; //!< The current request ID
|
||||
|
||||
public:
|
||||
|
||||
//! The singleton method
|
||||
static ZoneInstanceManager* Instance() {
|
||||
if (m_Address == 0) {
|
||||
if (m_Address == nullptr) {
|
||||
m_Address = new ZoneInstanceManager;
|
||||
m_Address->currentRequestID = 0;
|
||||
}
|
||||
@@ -47,7 +43,7 @@ public:
|
||||
\param mythranShift Whether or not this is a mythran shift
|
||||
\param callback The callback function
|
||||
*/
|
||||
void RequestZoneTransfer(dServer* server, uint32_t zoneID, uint32_t zoneClone, bool mythranShift, std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)> callback);
|
||||
void RequestZoneTransfer(dServer* server, uint32_t zoneID, uint32_t zoneClone, bool mythranShift, TransferCallback callback);
|
||||
|
||||
//! Handles a zone transfer response
|
||||
/*!
|
||||
@@ -58,6 +54,5 @@ public:
|
||||
|
||||
void CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password);
|
||||
|
||||
void RequestPrivateZone(dServer* server, bool mythranShift, const std::string& password, std::function<void(bool, uint32_t, uint32_t, uint32_t, std::string, uint16_t)> callback);
|
||||
|
||||
void RequestPrivateZone(dServer* server, bool mythranShift, const std::string& password, TransferCallback callback);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "dpEntity.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <ranges>
|
||||
|
||||
dpGrid::dpGrid(int numCells, int cellSize) {
|
||||
NUM_CELLS = numCells;
|
||||
@@ -122,38 +123,35 @@ void dpGrid::HandleEntity(dpEntity* entity, dpEntity* other) {
|
||||
void dpGrid::HandleCell(int x, int z, float deltaTime) {
|
||||
auto& entities = m_Cells[x][z]; //vector of entities contained within this cell.
|
||||
|
||||
for (auto en : entities) {
|
||||
for (auto* en : entities) {
|
||||
if (!en) continue;
|
||||
if (en->GetIsStatic() || en->GetSleeping()) continue;
|
||||
|
||||
//Check against all entities that are in the same cell as us
|
||||
for (auto other : entities)
|
||||
HandleEntity(en, other);
|
||||
for (auto other : entities) HandleEntity(en, other);
|
||||
|
||||
//To try neighbouring cells as well: (can be disabled if needed)
|
||||
//we only check 4 of the 8 neighbouring cells, otherwise we'd get duplicates and cpu cycles wasted...
|
||||
|
||||
if (x > 0 && z > 0) {
|
||||
for (auto other : m_Cells[x - 1][z - 1])
|
||||
HandleEntity(en, other);
|
||||
// All 8 neighbours in one pass.
|
||||
// staticOnly=false — canonical 4: covers each dynamic-vs-dynamic pair exactly once,
|
||||
// since the higher-index cell checks back to the lower-index cell.
|
||||
// staticOnly=true — skipped 4: dynamic entities there are handled when those cells
|
||||
// process their own en loop; static ones never drive a loop, so
|
||||
// we handle them here explicitly to avoid missing exits.
|
||||
struct NeighbourCheck { int dx, dz; bool staticOnly; };
|
||||
constexpr NeighbourCheck kNeighbours[8] = {
|
||||
{ -1, -1, false }, { -1, 0, false }, { 0, -1, false }, { -1, 1, false },
|
||||
{ 1, -1, true }, { 1, 0, true }, { 0, 1, true }, { 1, 1, true },
|
||||
};
|
||||
for (auto [dx, dz, staticOnly] : kNeighbours) {
|
||||
const int nx = x + dx;
|
||||
const int nz = z + dz;
|
||||
// Ensure the cell we're checking is within the valid range
|
||||
if (nx < 0 || nx >= NUM_CELLS || nz < 0 || nz >= NUM_CELLS) continue;
|
||||
for (auto* other : m_Cells[nx][nz]) {
|
||||
if (!staticOnly || (other && other->GetIsStatic()))
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
}
|
||||
|
||||
if (x > 0) {
|
||||
for (auto other : m_Cells[x - 1][z])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
if (z > 0) {
|
||||
for (auto other : m_Cells[x][z - 1])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
if (x > 0 && z < NUM_CELLS - 1) {
|
||||
for (auto other : m_Cells[x - 1][z + 1])
|
||||
HandleEntity(en, other);
|
||||
}
|
||||
|
||||
for (auto& [id, entity] : m_GargantuanObjects)
|
||||
HandleEntity(en, entity);
|
||||
for (auto* entity : m_GargantuanObjects | std::views::values) HandleEntity(en, entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ void NpcAgCourseStarter::OnUse(Entity* self, Entity* user) {
|
||||
const auto userId = user->GetObjectID();
|
||||
const auto& userSysAddr = user->GetSystemAddress();
|
||||
|
||||
if (scriptedActivityComponent->GetActivityPlayerData(userId) != nullptr) {
|
||||
if (scriptedActivityComponent->PlayerHasActivityData(userId)) {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "", userSysAddr);
|
||||
} else {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "", userSysAddr);
|
||||
@@ -45,18 +45,18 @@ void NpcAgCourseStarter::OnMessageBoxResponse(Entity* self, Entity* sender, int3
|
||||
GameMessages::SendNotifyClientObject(selfId, u"start_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr);
|
||||
GameMessages::SendActivityStart(selfId, senderSysAddr);
|
||||
|
||||
auto* const data = scriptedActivityComponent->AddActivityPlayerData(senderId);
|
||||
if (data->values[1] != 0) return;
|
||||
const auto score = scriptedActivityComponent->GetActivityValue(senderId, 1);
|
||||
if (score != 0 && score != -1.0f) return;
|
||||
|
||||
const auto raceStartTime = Game::server->GetUptime() + std::chrono::seconds(4); // Offset for starting timer
|
||||
const auto fRaceStartTime = std::chrono::duration<float, std::ratio<1>>(raceStartTime).count();
|
||||
data->values[1] = fRaceStartTime;
|
||||
scriptedActivityComponent->SetActivityValue(senderId, 1, fRaceStartTime);
|
||||
|
||||
Game::entityManager->SerializeEntity(self);
|
||||
} else if (identifier == u"FootRaceCancel") {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"stop_timer", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr);
|
||||
|
||||
if (scriptedActivityComponent->GetActivityPlayerData(senderId) != nullptr) {
|
||||
if (scriptedActivityComponent->PlayerHasActivityData(senderId)) {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"exit", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr);
|
||||
} else {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"start", 0, 0, LWOOBJID_EMPTY, "", senderSysAddr);
|
||||
@@ -74,8 +74,7 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std
|
||||
const auto senderId = sender->GetObjectID();
|
||||
const auto& senderSysAddr = sender->GetSystemAddress();
|
||||
|
||||
auto* const data = scriptedActivityComponent->GetActivityPlayerData(senderId);
|
||||
if (!data) return;
|
||||
if (!scriptedActivityComponent->PlayerHasActivityData(senderId)) return;
|
||||
|
||||
if (args == "course_cancel") {
|
||||
GameMessages::SendNotifyClientObject(selfId, u"cancel_timer", 0, 0,
|
||||
@@ -84,8 +83,11 @@ void NpcAgCourseStarter::OnFireEventServerSide(Entity* self, Entity* sender, std
|
||||
} else if (args == "course_finish") {
|
||||
const auto raceEndTime = Game::server->GetUptime();
|
||||
const auto fRaceEndTime = std::chrono::duration<float, std::ratio<1>>(raceEndTime).count();
|
||||
const auto raceTimeElapsed = fRaceEndTime - data->values[1];
|
||||
data->values[2] = raceTimeElapsed;
|
||||
const float startTime = scriptedActivityComponent->GetActivityValue(senderId, 1);
|
||||
if (startTime == 0 || startTime == -1.0f) return;
|
||||
|
||||
const auto raceTimeElapsed = fRaceEndTime - startTime;
|
||||
scriptedActivityComponent->SetActivityValue(senderId, 2, raceTimeElapsed);
|
||||
|
||||
auto* const missionComponent = sender->GetComponent<MissionComponent>();
|
||||
if (missionComponent != nullptr) {
|
||||
|
||||
@@ -48,7 +48,7 @@ void AmDrawBridge::OnTimerDone(Entity* self, std::string timerName) {
|
||||
}
|
||||
|
||||
self->SetNetworkVar(u"BridgeLeaving", true);
|
||||
self->SetVar(u"BridgeDown", false);
|
||||
self->SetNetworkVar(u"InUse", false);
|
||||
} else if (timerName == "SmashEffectBridge") {
|
||||
self->SetNetworkVar(u"SmashBridge", 5);
|
||||
} else if (timerName == "rotateBridgeDown") {
|
||||
|
||||
@@ -18,6 +18,7 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB
|
||||
"NjJayMissionItems.cpp"
|
||||
"NjNPCMissionSpinjitzuServer.cpp"
|
||||
"NjNyaMissionitems.cpp"
|
||||
"OldManNPC.cpp"
|
||||
"NjScrollChestServer.cpp"
|
||||
"NjWuNPC.cpp"
|
||||
"RainOfArrows.cpp")
|
||||
|
||||
31
dScripts/02_server/Map/njhub/OldManNPC.cpp
Normal file
31
dScripts/02_server/Map/njhub/OldManNPC.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "OldManNPC.h"
|
||||
|
||||
#include "eMissionState.h"
|
||||
#include "Character.h"
|
||||
#include "MissionComponent.h"
|
||||
|
||||
void ResetMissions(Entity& user) {
|
||||
for (int32_t i = 1; i < 7; i++) {
|
||||
int32_t flag = 2020 + i;
|
||||
auto* const character = user.GetCharacter();
|
||||
if (character) character->SetPlayerFlag(flag, false);
|
||||
}
|
||||
}
|
||||
|
||||
void OldManNPC::OnUse(Entity* self, Entity* user) {
|
||||
LOG("");
|
||||
const auto* const missionComponent = user->GetComponent<MissionComponent>();
|
||||
if (!missionComponent) return;
|
||||
|
||||
const auto* const mission = missionComponent->GetMission(2039);
|
||||
if (!mission) {
|
||||
ResetMissions(*user); // shouldnt be needed for dlu but it is because the mission is null
|
||||
return;
|
||||
}
|
||||
|
||||
const auto missionState = mission->GetMissionState();
|
||||
LOG("mission state %i", missionState);
|
||||
if (missionState == eMissionState::AVAILABLE || missionState == eMissionState::COMPLETE_AVAILABLE) {
|
||||
ResetMissions(*user);
|
||||
}
|
||||
}
|
||||
10
dScripts/02_server/Map/njhub/OldManNPC.h
Normal file
10
dScripts/02_server/Map/njhub/OldManNPC.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef OLDMANNPC_H
|
||||
#define OLDMANNPC_H
|
||||
|
||||
#include "CppScripts.h"
|
||||
|
||||
class OldManNPC : public CppScripts::Script {
|
||||
void OnUse(Entity* self, Entity* user) override;
|
||||
};
|
||||
|
||||
#endif //!OLDMANNPC_H
|
||||
@@ -114,7 +114,7 @@ uint32_t ActivityManager::CalculateActivityRating(Entity* self, const LWOOBJID p
|
||||
if (sac == nullptr)
|
||||
return 0;
|
||||
|
||||
return sac->GetInstance(playerID)->GetParticipants().size();
|
||||
return sac->GetInstance(playerID).GetParticipants().size();
|
||||
}
|
||||
|
||||
uint32_t ActivityManager::GetActivityID(const Entity* self) {
|
||||
|
||||
@@ -278,6 +278,7 @@
|
||||
#include "NjEarthPetServer.h"
|
||||
#include "NjDragonEmblemChestServer.h"
|
||||
#include "NjNyaMissionitems.h"
|
||||
#include "OldManNPC.h"
|
||||
|
||||
// Scripted equipment
|
||||
#include "AnvilOfArmor.h"
|
||||
@@ -628,6 +629,7 @@ namespace {
|
||||
{"scripts\\02_server\\Map\\njhub\\L_EARTH_PET_SERVER.lua", []() {return new NjEarthPetServer();}},
|
||||
{"scripts\\02_server\\Map\\njhub\\L_DRAGON_EMBLEM_CHEST_SERVER.lua", []() {return new NjDragonEmblemChestServer();}},
|
||||
{"scripts\\02_server\\Map\\njhub\\L_NYA_MISSION_ITEMS.lua", []() {return new NjNyaMissionitems();}},
|
||||
{"scripts\\02_server\\Map\\njhub\\L_OLD_MAN_NPC.lua", []() {return new OldManNPC();}},
|
||||
|
||||
//DLU
|
||||
{"scripts\\02_server\\DLU\\DLUVanityTeleportingObject.lua", []() {return new DLUVanityTeleportingObject();}},
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "dConfig.h"
|
||||
#include "dpWorld.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "Metrics.hpp"
|
||||
#include "Metrics.h"
|
||||
#include "PerformanceManager.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
@@ -1538,7 +1538,6 @@ void FinalizeShutdown() {
|
||||
LOG("Shutdown complete, zone (%i), instance (%i)", Game::server->GetZoneID(), g_InstanceID);
|
||||
|
||||
//Delete our objects here:
|
||||
Metrics::Clear();
|
||||
dpWorld::Shutdown();
|
||||
Database::Destroy("WorldServer");
|
||||
if (Game::chatFilter) delete Game::chatFilter;
|
||||
|
||||
@@ -55,7 +55,8 @@ Spawner::Spawner(const SpawnerInfo info) {
|
||||
m_SpawnSmashFoundGroup = true;
|
||||
m_SpawnOnSmashID = ssSpawner ? ssSpawner->m_Info.spawnerID : LWOOBJID_EMPTY;
|
||||
ssSpawner->AddSpawnedEntityDieCallback([=, this]() {
|
||||
Spawn();
|
||||
// Intentionally left as a non debug log since i have no idea how much stuff this would affect
|
||||
LOG("WOULD HAVE SPAWNED %i", m_EntityInfo.lot);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ services:
|
||||
- MYSQL_PASSWORD=${MARIADB_PASSWORD:?error}
|
||||
- EXTERNAL_IP=${EXTERNAL_IP:-localhost}
|
||||
- CLIENT_NET_VERSION=${CLIENT_NET_VERSION:-171022}
|
||||
- MAXIMUM_OUTGOING_BANDWIDTH=${MAXIMUM_OUTGOING_BANDWIDTH:0}
|
||||
depends_on:
|
||||
- darkflamedb
|
||||
ports:
|
||||
|
||||
Reference in New Issue
Block a user