mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[Zone] Zone State Automated Testing and Improvements (#4808)
* [Zone] Zone State Automated Testing and Improvements * Spawn condition * Update zone.cpp * Remove redundant logic * Update zone_state.cpp * TestZLocationDrift * Protect NPC resumed NPC's from being able to die
This commit is contained in:
parent
c8a7066d0e
commit
b9cfdea76c
@ -196,3 +196,25 @@ const uint32 Timer::SetCurrentTime()
|
||||
return current_time;
|
||||
}
|
||||
|
||||
const uint32 Timer::RollForward(uint32 seconds)
|
||||
{
|
||||
struct timeval read_time{};
|
||||
uint32 this_time;
|
||||
|
||||
gettimeofday(&read_time, nullptr);
|
||||
this_time = read_time.tv_sec * 1000 + read_time.tv_usec / 1000;
|
||||
|
||||
if (last_time == 0) {
|
||||
current_time = 0;
|
||||
}
|
||||
else {
|
||||
current_time += this_time - last_time;
|
||||
}
|
||||
|
||||
last_time = this_time;
|
||||
|
||||
// Roll forward the specified number of seconds (converted to milliseconds)
|
||||
current_time += seconds * 1000;
|
||||
|
||||
return current_time;
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ public:
|
||||
inline uint32 GetDuration() { return(timer_time); }
|
||||
|
||||
static const uint32 SetCurrentTime();
|
||||
static const uint32 RollForward(uint32 seconds);
|
||||
static const uint32 GetCurrentTime();
|
||||
static const uint32 GetTimeSeconds();
|
||||
|
||||
|
||||
@ -57,6 +57,7 @@ echo "# Running NPC hand-in tests"
|
||||
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
||||
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
||||
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
||||
./bin/zone tests:zone-state 2>&1 | tee -a test_output.log
|
||||
|
||||
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
||||
echo "Error found in test output! Failing build."
|
||||
|
||||
@ -173,6 +173,7 @@ SET(zone_sources
|
||||
zone_event_scheduler.cpp
|
||||
zone_npc_factions.cpp
|
||||
zone_reload.cpp
|
||||
zone_save_state.cpp
|
||||
zoning.cpp
|
||||
)
|
||||
|
||||
@ -292,6 +293,7 @@ SET(zone_headers
|
||||
zonedump.h
|
||||
zone_cli.h
|
||||
zone_reload.h
|
||||
zone_save_state.h
|
||||
zone_cli.cpp)
|
||||
|
||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||
|
||||
@ -2507,6 +2507,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_resumed_from_zone_suspend && !IsQueuedForCorpse()) {
|
||||
LogInfo("NPC [{}] is resumed from zone suspend, cannot kill until zone resume is complete.", GetCleanName());
|
||||
SetHP(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsMultiQuestEnabled()) {
|
||||
for (auto &i: m_hand_in.items) {
|
||||
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
||||
@ -2627,7 +2633,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
bool pet_owner_is_client = give_exp->IsPet() && owner->IsClient();
|
||||
bool pet_owner_is_bot = give_exp->IsPet() && owner->IsBot();
|
||||
bool owner_is_client = owner->IsClient();
|
||||
|
||||
|
||||
bool is_in_same_group_or_raid = (
|
||||
pet_owner_is_client ||
|
||||
(pet_owner_is_bot && owner->IsInGroupOrRaid(ulimate_owner)) ||
|
||||
|
||||
57
zone/cli/tests/_test_util.cpp
Normal file
57
zone/cli/tests/_test_util.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "../../zone.h"
|
||||
|
||||
inline void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||
} else {
|
||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||
std::cerr << " ❌ Got: " << actual << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RunTest(const std::string &test_name, bool expected, bool actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||
}
|
||||
else {
|
||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
||||
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RunTest(const std::string &test_name, int expected, int actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||
}
|
||||
else {
|
||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||
std::cerr << " ❌ Got: " << actual << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
extern Zone *zone;
|
||||
|
||||
inline void SetupZone(std::string zone_short_name, uint32 instance_id = 0) {
|
||||
LogSys.SilenceConsoleLogging();
|
||||
|
||||
LogSys.log_settings[Logs::ZoneState].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||
LogSys.log_settings[Logs::Info].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||
LogSys.log_settings[Logs::Spawns].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||
|
||||
// boot shell zone for testing
|
||||
Zone::Bootup(ZoneID(zone_short_name), 0, false);
|
||||
zone->StopShutdownTimer();
|
||||
entity_list.Process();
|
||||
entity_list.MobProcess();
|
||||
|
||||
LogSys.EnableConsoleLogging();
|
||||
}
|
||||
@ -1,25 +1,13 @@
|
||||
#include "../../common/http/httplib.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/platform.h"
|
||||
#include "../zone.h"
|
||||
#include "../client.h"
|
||||
#include "../../zone.h"
|
||||
#include "../../client.h"
|
||||
#include "../../common/net/eqstream.h"
|
||||
|
||||
extern Zone *zone;
|
||||
|
||||
void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||
} else {
|
||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||
std::cerr << " ❌ Got: " << actual << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneCLI::DataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
void ZoneCLI::TestDataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
return;
|
||||
@ -1,8 +1,8 @@
|
||||
#include "../../common/http/httplib.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/platform.h"
|
||||
#include "../zone.h"
|
||||
#include "../client.h"
|
||||
#include "../../zone.h"
|
||||
#include "../../client.h"
|
||||
#include "../../common/net/eqstream.h"
|
||||
#include "../../common/json/json.hpp"
|
||||
|
||||
@ -36,19 +36,6 @@ struct TestCase {
|
||||
bool handin_check_result;
|
||||
};
|
||||
|
||||
void RunTest(const std::string &test_name, bool expected, bool actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||
}
|
||||
else {
|
||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
||||
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void RunSerializedTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||
{
|
||||
if (expected == actual) {
|
||||
@ -75,7 +62,7 @@ std::string SerializeHandin(const std::map<std::string, uint32> &items, const Ha
|
||||
return j.dump();
|
||||
}
|
||||
|
||||
void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
void ZoneCLI::TestNpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
return;
|
||||
@ -1,13 +1,13 @@
|
||||
#include "../../common/http/httplib.h"
|
||||
#include "../../common/eqemu_logsys.h"
|
||||
#include "../../common/platform.h"
|
||||
#include "../zone.h"
|
||||
#include "../client.h"
|
||||
#include "../../zone.h"
|
||||
#include "../../client.h"
|
||||
#include "../../common/net/eqstream.h"
|
||||
|
||||
extern Zone *zone;
|
||||
|
||||
void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
void ZoneCLI::TestNpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
return;
|
||||
1094
zone/cli/tests/zone_state.cpp
Normal file
1094
zone/cli/tests/zone_state.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -5991,3 +5991,14 @@ void EntityList::SendMerchantInventory(Mob* m, int32 slot_id, bool is_delete)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void EntityList::RestoreCorpse(NPC *npc, uint32_t decay_time)
|
||||
{
|
||||
uint16 corpse_id = npc->GetID();
|
||||
npc->Death(npc, npc->GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||
if (c) {
|
||||
c->UnLock();
|
||||
c->SetDecayTimer(decay_time);
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,6 +580,7 @@ public:
|
||||
|
||||
void SendMerchantEnd(Mob* merchant);
|
||||
void SendMerchantInventory(Mob* m, int32 slot_id = -1, bool is_delete = false);
|
||||
void RestoreCorpse(NPC* npc, uint32_t decay_time);
|
||||
|
||||
protected:
|
||||
friend class Zone;
|
||||
|
||||
@ -1125,7 +1125,7 @@ public:
|
||||
|
||||
virtual void SetAttackTimer();
|
||||
inline void SetInvul(bool invul) { invulnerable=invul; }
|
||||
inline bool GetInvul(void) { return invulnerable; }
|
||||
inline bool GetInvul() { return invulnerable; }
|
||||
void SetExtraHaste(int haste, bool need_to_save = true);
|
||||
inline int GetExtraHaste() { return extra_haste; }
|
||||
virtual int GetHaste();
|
||||
|
||||
35
zone/npc.cpp
35
zone/npc.cpp
@ -132,8 +132,6 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
||||
),
|
||||
attacked_timer(CombatEventTimer_expire),
|
||||
swarm_timer(100),
|
||||
m_corpse_queue_timer(1000),
|
||||
m_corpse_queue_shutoff_timer(30000),
|
||||
m_resumed_from_zone_suspend_shutoff_timer(10000),
|
||||
classattack_timer(1000),
|
||||
monkattack_timer(1000),
|
||||
@ -624,28 +622,6 @@ bool NPC::Process()
|
||||
|
||||
// zone state corpse creation timer
|
||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||
// creates a corpse if the NPC is queued for corpse creation
|
||||
if (m_corpse_queue_timer.Check()) {
|
||||
if (IsQueuedForCorpse()) {
|
||||
auto decay_timer = m_corpse_decay_time;
|
||||
uint16 corpse_id = GetID();
|
||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||
if (c) {
|
||||
c->UnLock();
|
||||
c->SetDecayTimer(decay_timer);
|
||||
}
|
||||
}
|
||||
m_corpse_queue_timer.Disable();
|
||||
m_corpse_queue_shutoff_timer.Disable();
|
||||
}
|
||||
|
||||
// shuts off the corpse queue timer if it is still running
|
||||
if (m_corpse_queue_shutoff_timer.Check()) {
|
||||
m_corpse_queue_timer.Disable();
|
||||
m_corpse_queue_shutoff_timer.Disable();
|
||||
}
|
||||
|
||||
// shuts off the temporary spawn protected state of the NPC
|
||||
if (m_resumed_from_zone_suspend_shutoff_timer.Check()) {
|
||||
m_resumed_from_zone_suspend_shutoff_timer.Disable();
|
||||
@ -654,17 +630,6 @@ bool NPC::Process()
|
||||
}
|
||||
|
||||
if (tic_timer.Check()) {
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && IsQueuedForCorpse()) {
|
||||
auto decay_timer = m_corpse_decay_time;
|
||||
uint16 corpse_id = GetID();
|
||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||
if (c) {
|
||||
c->UnLock();
|
||||
c->SetDecayTimer(decay_timer);
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TICK)) {
|
||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||
}
|
||||
|
||||
16
zone/npc.h
16
zone/npc.h
@ -603,10 +603,9 @@ public:
|
||||
|
||||
// zone state save
|
||||
inline void SetQueuedToCorpse() { m_queued_for_corpse = true; }
|
||||
inline bool IsQueuedForCorpse() { return m_queued_for_corpse; }
|
||||
inline uint32_t SetCorpseDecayTime(uint32_t decay_time) { return m_corpse_decay_time = decay_time; }
|
||||
inline bool IsQueuedForCorpse() const { return m_queued_for_corpse; }
|
||||
inline void SetResumedFromZoneSuspend(bool state = true) { m_resumed_from_zone_suspend = state; }
|
||||
inline bool IsResumedFromZoneSuspend() { return m_resumed_from_zone_suspend; }
|
||||
inline bool IsResumedFromZoneSuspend() const { return m_resumed_from_zone_suspend; }
|
||||
|
||||
inline void LoadBuffsFromState(std::vector<Buffs_Struct> in_buffs) {
|
||||
int i = 0;
|
||||
@ -658,15 +657,12 @@ protected:
|
||||
LootItems m_loot_items;
|
||||
|
||||
// zone state
|
||||
bool m_resumed_from_zone_suspend = false;
|
||||
bool m_queued_for_corpse = false; // this is to check for corpse creation on zone state restore
|
||||
uint32_t m_corpse_decay_time = 0; // decay time set on zone state restore
|
||||
Timer m_corpse_queue_timer = {}; // this is to check for corpse creation on zone state restore
|
||||
Timer m_corpse_queue_shutoff_timer = {};
|
||||
bool m_resumed_from_zone_suspend = false;
|
||||
bool m_queued_for_corpse = false; // this is to check for corpse creation on zone state restore
|
||||
|
||||
// this is a 30-second timer that protects a NPC from having double assignment of loot
|
||||
// this is a timer that protects a NPC from having double assignment of loot
|
||||
// this is to prevent a player from killing a NPC and then zoning out and back in to get loot again
|
||||
// if loot was to be assigned via script again, this protects double assignment for 30 seconds
|
||||
// if loot was to be assigned via script again, this protects double assignment for a short time
|
||||
Timer m_resumed_from_zone_suspend_shutoff_timer = {};
|
||||
|
||||
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
|
||||
|
||||
@ -191,16 +191,20 @@ bool Spawn2::Process() {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16 condition_value=1;
|
||||
|
||||
uint16 condition_value = 1;
|
||||
if (condition_id > 0) {
|
||||
condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id);
|
||||
condition_value = zone->spawn_conditions.GetCondition(
|
||||
zone->GetShortName(),
|
||||
zone->GetInstanceID(),
|
||||
condition_id
|
||||
);
|
||||
}
|
||||
|
||||
//have the spawn group pick an NPC for us
|
||||
uint32 npcid = 0;
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && currentnpcid && currentnpcid > 0) {
|
||||
npcid = currentnpcid;
|
||||
if (m_resumed_npc_id > 0) {
|
||||
npcid = m_resumed_npc_id;
|
||||
m_resumed_npc_id = 0;
|
||||
} else {
|
||||
npcid = spawn_group->GetNPCType(condition_value);
|
||||
}
|
||||
|
||||
@ -78,6 +78,7 @@ public:
|
||||
inline bool IsResumedFromZoneSuspend() const { return m_resumed_from_zone_suspend; }
|
||||
inline void SetResumedFromZoneSuspend(bool resumed) { m_resumed_from_zone_suspend = resumed; }
|
||||
inline void SetEntityVariables(std::map<std::string, std::string> vars) { m_entity_variables = vars; }
|
||||
inline void SetResumedNPCID(uint32 npc_id) { m_resumed_npc_id = npc_id; }
|
||||
|
||||
protected:
|
||||
friend class Zone;
|
||||
@ -105,6 +106,7 @@ private:
|
||||
bool IsDespawned;
|
||||
uint32 killcount;
|
||||
bool m_resumed_from_zone_suspend = false;
|
||||
uint32 m_resumed_npc_id = 0;
|
||||
std::map<std::string, std::string> m_entity_variables = {};
|
||||
};
|
||||
|
||||
|
||||
@ -887,10 +887,7 @@ void Zone::Shutdown(bool quiet)
|
||||
c.second->WorldKick();
|
||||
}
|
||||
|
||||
bool does_zone_have_entities =
|
||||
zone && zone->IsLoaded() &&
|
||||
(!entity_list.GetNPCList().empty() || !entity_list.GetCorpseList().empty());
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && does_zone_have_entities) {
|
||||
if (RuleB(Zone, StateSavingOnShutdown) && zone && zone->IsLoaded()) {
|
||||
SaveZoneState();
|
||||
}
|
||||
|
||||
@ -1537,7 +1534,6 @@ bool Zone::Process() {
|
||||
spawn_conditions.Process();
|
||||
|
||||
if (spawn2_timer.Check()) {
|
||||
|
||||
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
|
||||
|
||||
EQ::InventoryProfile::CleanDirty();
|
||||
@ -3295,5 +3291,4 @@ void Zone::ReloadMaps()
|
||||
pathing = IPathfinder::Load(map_name);
|
||||
}
|
||||
|
||||
#include "zone_save_state.cpp"
|
||||
#include "zone_loot.cpp"
|
||||
|
||||
@ -29,17 +29,23 @@ void ZoneCLI::CommandHandler(int argc, char **argv)
|
||||
auto function_map = EQEmuCommand::function_map;
|
||||
|
||||
// Register commands
|
||||
function_map["benchmark:databuckets"] = &ZoneCLI::BenchmarkDatabuckets;
|
||||
function_map["sidecar:serve-http"] = &ZoneCLI::SidecarServeHttp;
|
||||
function_map["tests:databuckets"] = &ZoneCLI::DataBuckets;
|
||||
function_map["tests:npc-handins"] = &ZoneCLI::NpcHandins;
|
||||
function_map["tests:npc-handins-multiquest"] = &ZoneCLI::NpcHandinsMultiQuest;
|
||||
function_map["benchmark:databuckets"] = &ZoneCLI::BenchmarkDatabuckets;
|
||||
function_map["sidecar:serve-http"] = &ZoneCLI::SidecarServeHttp;
|
||||
function_map["tests:databuckets"] = &ZoneCLI::TestDataBuckets;
|
||||
function_map["tests:npc-handins"] = &ZoneCLI::TestNpcHandins;
|
||||
function_map["tests:npc-handins-multiquest"] = &ZoneCLI::TestNpcHandinsMultiQuest;
|
||||
function_map["tests:zone-state"] = &ZoneCLI::TestZoneState;
|
||||
|
||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||
}
|
||||
|
||||
#include "cli/databuckets.cpp"
|
||||
// cli
|
||||
#include "cli/benchmark_databuckets.cpp"
|
||||
#include "cli/sidecar_serve_http.cpp"
|
||||
#include "cli/npc_handins.cpp"
|
||||
#include "cli/npc_handins_multiquest.cpp"
|
||||
|
||||
// tests
|
||||
#include "cli/tests/_test_util.cpp"
|
||||
#include "cli/tests/databuckets.cpp"
|
||||
#include "cli/tests/npc_handins.cpp"
|
||||
#include "cli/tests/npc_handins_multiquest.cpp"
|
||||
#include "cli/tests/zone_state.cpp"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#ifndef EQEMU_ZONE_CLI_H
|
||||
#define EQEMU_ZONE_CLI_H
|
||||
|
||||
#include <iostream>
|
||||
#include "../common/cli/argh.h"
|
||||
|
||||
class ZoneCLI {
|
||||
@ -11,10 +12,10 @@ public:
|
||||
static bool RanConsoleCommand(int argc, char **argv);
|
||||
static bool RanSidecarCommand(int argc, char **argv);
|
||||
static bool RanTestCommand(int argc, char **argv);
|
||||
static void DataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void TestDataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void TestNpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void TestNpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
static void TestZoneState(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_ZONE_CLI_H
|
||||
|
||||
@ -368,18 +368,22 @@ void Zone::LoadLootDrops(const std::vector<uint32> in_lootdrop_ids)
|
||||
m_lootdrops.emplace_back(e);
|
||||
|
||||
// add lootdrop entries
|
||||
for (const auto &f: lootdrop_entries) {
|
||||
if (e.id == f.lootdrop_id) {
|
||||
// add lootdrop entries
|
||||
for (const auto &h: lootdrop_entries) {
|
||||
if (e.id == h.lootdrop_id) {
|
||||
|
||||
// check if lootdrop entry already exists in memory
|
||||
has_entry = false;
|
||||
for (const auto &g: m_lootdrop_entries) {
|
||||
if (f.lootdrop_id == g.lootdrop_id && f.item_id == g.item_id && f.multiplier == g.multiplier) {
|
||||
for (const auto &i: m_lootdrop_entries) {
|
||||
if (h.lootdrop_id == i.lootdrop_id && h.item_id == i.item_id) {
|
||||
has_entry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_entry) {
|
||||
m_lootdrop_entries.emplace_back(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,46 +4,7 @@
|
||||
#include "npc.h"
|
||||
#include "corpse.h"
|
||||
#include "zone.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||
|
||||
struct LootEntryStateData {
|
||||
uint32 item_id;
|
||||
uint32_t lootdrop_id;
|
||||
uint16 charges = 0; // used in dynamically added loot (AddItem)
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(lootdrop_id),
|
||||
CEREAL_NVP(charges)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LootStateData {
|
||||
uint32 copper = 0;
|
||||
uint32 silver = 0;
|
||||
uint32 gold = 0;
|
||||
uint32 platinum = 0;
|
||||
std::vector<LootEntryStateData> entries = {};
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(copper),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(platinum),
|
||||
CEREAL_NVP(entries)
|
||||
);
|
||||
}
|
||||
};
|
||||
#include "zone_save_state.h"
|
||||
|
||||
// IsZoneStateValid checks if the zone state is valid
|
||||
// if these fields are all empty or zero value for an entire zone state, it's considered invalid
|
||||
@ -359,7 +320,7 @@ inline void LoadNPCState(Zone *zone, NPC *n, ZoneStateSpawnsRepository::ZoneStat
|
||||
auto decay_time = s.decay_in_seconds * 1000;
|
||||
if (decay_time > 0) {
|
||||
n->SetQueuedToCorpse();
|
||||
n->SetCorpseDecayTime(decay_time);
|
||||
entity_list.RestoreCorpse(n, decay_time);
|
||||
}
|
||||
else {
|
||||
n->Depop();
|
||||
@ -454,6 +415,7 @@ bool Zone::LoadZoneState(
|
||||
zone->Process();
|
||||
|
||||
// load zone variables first
|
||||
int count = 0;
|
||||
for (auto &s: spawn_states) {
|
||||
if (s.is_zone) {
|
||||
LoadZoneVariables(zone, s.entity_variables);
|
||||
@ -502,11 +464,13 @@ bool Zone::LoadZoneState(
|
||||
);
|
||||
|
||||
if (spawn_time_left == 0) {
|
||||
new_spawn->SetCurrentNPCID(s.npc_id);
|
||||
new_spawn->SetResumedNPCID(s.npc_id);
|
||||
new_spawn->SetResumedFromZoneSuspend(true);
|
||||
new_spawn->SetEntityVariables(GetVariablesDeserialized(s.entity_variables));
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
spawn2_list.Insert(new_spawn);
|
||||
new_spawn->Process();
|
||||
auto n = new_spawn->GetNPC();
|
||||
@ -548,24 +512,6 @@ bool Zone::LoadZoneState(
|
||||
LoadNPCState(zone, npc, s);
|
||||
}
|
||||
|
||||
// any NPC that is spawned by the spawn system
|
||||
for (auto &e: entity_list.GetNPCList()) {
|
||||
auto npc = e.second;
|
||||
if (npc->GetSpawnGroupId() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &s: spawn_states) {
|
||||
bool is_same_npc =
|
||||
s.npc_id == npc->GetNPCTypeID() &&
|
||||
s.spawn2_id == npc->GetSpawnPointID() &&
|
||||
s.spawngroup_id == npc->GetSpawnGroupId();
|
||||
if (is_same_npc) {
|
||||
LoadNPCState(zone, npc, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !spawn_states.empty();
|
||||
}
|
||||
|
||||
@ -638,6 +584,7 @@ void Zone::SaveZoneState()
|
||||
std::vector<ZoneStateSpawnsRepository::ZoneStateSpawns> spawns = {};
|
||||
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
|
||||
iterator.Reset();
|
||||
int count = 0;
|
||||
while (iterator.MoreElements()) {
|
||||
Spawn2 *sp = iterator.GetData();
|
||||
auto s = ZoneStateSpawnsRepository::NewEntity();
|
||||
@ -667,6 +614,7 @@ void Zone::SaveZoneState()
|
||||
|
||||
spawns.emplace_back(s);
|
||||
iterator.Advance();
|
||||
count++;
|
||||
}
|
||||
|
||||
// npc's that are not in the spawn2 list
|
||||
@ -742,6 +690,11 @@ void Zone::SaveZoneState()
|
||||
return;
|
||||
}
|
||||
|
||||
if (spawns.empty()) {
|
||||
LogInfo("No zone state data to save");
|
||||
return;
|
||||
}
|
||||
|
||||
ZoneStateSpawnsRepository::InsertMany(database, spawns);
|
||||
|
||||
LogInfo("Saved [{}] zone state spawns", Strings::Commify(spawns.size()));
|
||||
|
||||
48
zone/zone_save_state.h
Normal file
48
zone/zone_save_state.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cereal/archives/json.hpp>
|
||||
#include <cereal/types/map.hpp>
|
||||
#include "npc.h"
|
||||
#include "corpse.h"
|
||||
#include "zone.h"
|
||||
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||
#include "../common/repositories/spawn2_disabled_repository.h"
|
||||
|
||||
struct LootEntryStateData {
|
||||
uint32 item_id = 0;
|
||||
uint32_t lootdrop_id = 0;
|
||||
uint16 charges = 0; // used in dynamically added loot (AddItem)
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(lootdrop_id),
|
||||
CEREAL_NVP(charges)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct LootStateData {
|
||||
uint32 copper = 0;
|
||||
uint32 silver = 0;
|
||||
uint32 gold = 0;
|
||||
uint32 platinum = 0;
|
||||
std::vector<LootEntryStateData> entries = {};
|
||||
|
||||
// cereal
|
||||
template<class Archive>
|
||||
void serialize(Archive &ar)
|
||||
{
|
||||
ar(
|
||||
CEREAL_NVP(copper),
|
||||
CEREAL_NVP(silver),
|
||||
CEREAL_NVP(gold),
|
||||
CEREAL_NVP(platinum),
|
||||
CEREAL_NVP(entries)
|
||||
);
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user