Compare commits

...

12 Commits

Author SHA1 Message Date
David Markowitz
90db1ac699 fix: incorrect network variable being set (#1994) 2026-06-13 00:21:53 -07:00
Daniel Seiler
9f8d300340 fix(docker): Unset MAXIMUM_OUTGOING_BANDWIDTH by default (#1548) 2026-06-11 18:38:38 -07:00
David Markowitz
1e9b18fa9d chore: cleanup usage of pointers in the activity component (#1989)
* chore: cleanup usage of pointers in the activity component

* feedback
2026-06-11 09:12:43 -05:00
David Markowitz
e5b8e5c6b7 fix: buggy hitboxes during ag foot race (#1993)
* physics fixes

* check ptr
2026-06-11 09:12:31 -05:00
David Markowitz
707880b5fc fix: fv pipe quick build not spawning as it should (#1991)
tested that the pipe now spawns a ROCK that you can build.  This ROCK you build spawns the PIPE now.
new bug: if you start building the ROCK and stop, the pipe will spawn instead of the previous rock.
2026-06-11 09:12:06 -05:00
David Markowitz
90607bdd5c fix: setting smashable ignoring lnv settings (#1992)
tested that the computer build on crux no longer stays around for +12 seconds
2026-06-11 09:11:52 -05:00
David Markowitz
bb8f569354 chore: Remove pointer usage in trading (#1988)
* chore: Remove pointer usage in trading

tested that I could still do a trade

* Update TradingManager.h

* remove returned object
2026-06-09 16:05:21 -05:00
David Markowitz
93076dc36d chore: simplify metrics (#1987)
* chore: simplify metrics

rename to hpp and remove unused includes

* feedback
2026-06-08 21:42:32 -07:00
David Markowitz
a307f0601a fix: bugs in private instances causing master crashes (#1986)
* fix: bugs in private instances causing master crashes

tested that creating a private instance and shutting down the server no longer crashes master

* Update InstanceManager.cpp
2026-06-08 23:22:04 -05:00
David Markowitz
045e097b13 chore: cleanup zoneIM (#1985) 2026-06-08 22:57:33 -05:00
Aaron Kimbrell
ca0da9d3bf feat: preconditions improvements (#1983)
* feat: implement missing precondition types (20, 21, 23) and pet checks

Add DoesNotHaveFlag (23), NotFreeTrial (20), and MissionActive (21) to
PreconditionType and implement their checks. Also implement PetDeployed
and IsPetTaming using PetComponent static helpers, matching client
behavior — both are simple boolean checks with no LOT comparison.
LegoClubMember is set to always pass as DLU has no membership concept.

* fix: update TODO comments for team check and racing licence preconditions

* type

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-08 22:45:24 -05:00
David Markowitz
1d2de705fb fix: old man npc mission (#1982)
* fix: old man npc mission

tested that the repeatable daily now has to actually be done and also can actually be done.

* Update OldManNPC.cpp
2026-06-08 20:44:29 -07:00
35 changed files with 485 additions and 541 deletions

View File

@@ -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
View 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();
};

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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"

View File

@@ -18,7 +18,8 @@
#include "DluAssert.h"
#include "CDActivitiesTable.h"
#include "Metrics.hpp"
#include <chrono>
namespace LeaderboardManager {
std::map<GameID, Leaderboard::Type> leaderboardCache;

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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;

View File

@@ -64,6 +64,7 @@ public:
private:
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
/**
* All the proximity sensors for this component, indexed by name
*/

View File

@@ -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;
}

View File

@@ -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.
*/

View File

@@ -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;

View File

@@ -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;

View File

@@ -26,7 +26,10 @@ enum class PreconditionType
DoesNotHaveRacingLicence,
LegoClubMember,
NoInteraction,
HasLevel = 22
NotFreeTrial,
MissionActive,
HasLevel,
DoesNotHaveFlag = 23
};

View File

@@ -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";

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
};

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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") {

View File

@@ -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")

View 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);
}
}

View 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

View File

@@ -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) {

View File

@@ -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();}},

View File

@@ -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;

View File

@@ -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);
});
}
}

View File

@@ -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: