From d64205124f97bf97c342e030d50dcc5567f79c7a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 7 Nov 2015 13:20:24 -0600 Subject: [PATCH] Implemented #repopclose [distance in units] - Used for development purposes, defaults to 500 units - Real case use: Large zones with 700 NPC's and you are making fast quick tweaks to nearby NPC's you can refresh just the NPC's around you instead of all in the zone - This can be quite the time saver - This command will depop all NPC's and only respawn the NPC's that are 500 units around you or unless you specify otherwise --- changelog.txt | 6 +++ zone/command.cpp | 36 ++++++++++++++-- zone/command.h | 1 + zone/spawn2.cpp | 106 +++++++++++++++++++++++++++++++++++++++++++++++ zone/zone.cpp | 23 ++++++++++ zone/zone.h | 1 + zone/zonedb.h | 1 + 7 files changed, 171 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 5a1c346de..742edb5a5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 11/7/2015 == +Akkadius: Implemented #repopclose [distance in units] - Used for development purposes, defaults to 500 units + - Real case use: Large zones with 700 NPC's and you are making fast quick tweaks to nearby NPC's you can refresh just the NPC's around you instead of all in the zone + - This can be quite the time saver + - This command will depop all NPC's and only respawn the NPC's that are 500 units around you or unless you specify otherwise + == 11/2/2015 == Akkadius: Performance boost (exponential) - Adjusted default idle cast check timers in rules - Spells:AI_IdleNoSpellMinRecast 500 (Now 6000) 6 seconds diff --git a/zone/command.cpp b/zone/command.cpp index db41619f4..1aaf159b0 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -344,6 +344,7 @@ int command_init(void) { command_add("reloadzonepoints", "- Reload zone points from database", 150, command_reloadzps) || command_add("reloadzps", nullptr,0, command_reloadzps) || command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) || + command_add("repopclose", "[distance in units] Repops only NPC's nearby for fast development purposes", 100, command_repopclose) || command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) || command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) || command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) || @@ -3921,9 +3922,11 @@ void command_repop(Client *c, const Seperator *sep) LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); while (iterator.MoreElements()) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", - (unsigned long)iterator.GetData()->GetID(), - (unsigned long)zone->GetInstanceID()); + std::string query = StringFormat( + "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID() + ); auto results = database.QueryDatabase(query); iterator.Advance(); } @@ -3940,6 +3943,33 @@ void command_repop(Client *c, const Seperator *sep) zone->Repop(atoi(sep->arg[timearg])*1000); } +void command_repopclose(Client *c, const Seperator *sep) +{ + int repop_distance = 500; + + if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { + + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) { + std::string query = StringFormat( + "DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID() + ); + auto results = database.QueryDatabase(query); + iterator.Advance(); + } + c->Message(0, "Zone depop: Force resetting spawn timers."); + } + if (sep->IsNumber(1)) { + repop_distance = atoi(sep->arg[1]); + } + + c->Message(0, "Zone depoped. Repopping NPC's within %i distance units", repop_distance); + zone->RepopClose(c->GetPosition(), repop_distance); +} + void command_spawnstatus(Client *c, const Seperator *sep) { if((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) diff --git a/zone/command.h b/zone/command.h index e4130cfff..ac6107a20 100644 --- a/zone/command.h +++ b/zone/command.h @@ -187,6 +187,7 @@ void command_myskills(Client *c, const Seperator *sep); void command_depop(Client *c, const Seperator *sep); void command_depopzone(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); +void command_repopclose(Client *c, const Seperator *sep); void command_spawnstatus(Client *c, const Seperator *sep); void command_nukebuffs(Client *c, const Seperator *sep); void command_zuwcoords(Client *c, const Seperator *sep); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index c42356ea9..f2461ed4b 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -351,6 +351,112 @@ void Spawn2::DeathReset(bool realdeath) } } +bool ZoneDatabase::PopulateZoneSpawnListClose(uint32 zoneid, LinkedList &spawn2_list, int16 version, const glm::vec4& client_position, uint32 repop_distance) +{ + std::unordered_map spawn_times; + + float mob_distance = 0; + + timeval tv; + gettimeofday(&tv, nullptr); + + /* Bulk Load NPC Types Data into the cache */ + database.LoadNPCTypesData(0, true); + + std::string spawn_query = StringFormat( + "SELECT " + "respawn_times.id, " + "respawn_times.`start`, " + "respawn_times.duration " + "FROM " + "respawn_times " + "WHERE instance_id = %u", + zone->GetInstanceID() + ); + auto results = QueryDatabase(spawn_query); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 start_duration = atoi(row[1]) > 0 ? atoi(row[1]) : 0; + uint32 end_duration = atoi(row[2]) > 0 ? atoi(row[2]) : 0; + + /* Our current time was expired */ + if ((start_duration + end_duration) <= tv.tv_sec) { + spawn_times[atoi(row[0])] = 0; + } + /* We still have time left on this timer */ + else { + spawn_times[atoi(row[0])] = ((start_duration + end_duration) - tv.tv_sec) * 1000; + } + } + + const char *zone_name = database.GetZoneName(zoneid); + std::string query = StringFormat( + "SELECT " + "id, " + "spawngroupID, " + "x, " + "y, " + "z, " + "heading, " + "respawntime, " + "variance, " + "pathgrid, " + "_condition, " + "cond_value, " + "enabled, " + "animation " + "FROM " + "spawn2 " + "WHERE zone = '%s' AND version = %u", + zone_name, + version + ); + results = QueryDatabase(query); + + if (!results.Success()) { + return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + + uint32 spawn_time_left = 0; + Spawn2* new_spawn = 0; + bool perl_enabled = atoi(row[11]) == 1 ? true : false; + + if (spawn_times.count(atoi(row[0])) != 0) + spawn_time_left = spawn_times[atoi(row[0])]; + + glm::vec4 point; + point.x = atof(row[2]); + point.y = atof(row[3]); + + mob_distance = DistanceNoZ(client_position, point); + + if (mob_distance > repop_distance) + continue; + + new_spawn = new Spawn2( // + atoi(row[0]), // uint32 in_spawn2_id + atoi(row[1]), // uint32 spawngroup_id + atof(row[2]), // float in_x + atof(row[3]), // float in_y + atof(row[4]), // float in_z + atof(row[5]), // float in_heading + atoi(row[6]), // uint32 respawn + atoi(row[7]), // uint32 variance + spawn_time_left, // uint32 timeleft + atoi(row[8]), // uint32 grid + atoi(row[9]), // uint16 in_cond_id + atoi(row[10]), // int16 in_min_value + perl_enabled, // bool in_enabled + (EmuAppearance)atoi(row[12]) // EmuAppearance anim + ); + + spawn2_list.Insert(new_spawn); + } + + return true; +} + bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay) { std::unordered_map spawn_times; diff --git a/zone/zone.cpp b/zone/zone.cpp index 14c0674c6..b3a6fc1ca 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1445,6 +1445,29 @@ void Zone::ClearNPCTypeCache(int id) { } } +void Zone::RepopClose(const glm::vec4& client_position, uint32 repop_distance) +{ + + if (!Depop()) + return; + + LinkedListIterator iterator(spawn2_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + iterator.RemoveCurrent(); + } + + quest_manager.ClearAllTimers(); + + if (!database.PopulateZoneSpawnListClose(zoneid, spawn2_list, GetInstanceVersion(), client_position, repop_distance)) + Log.Out(Logs::General, Logs::None, "Error in Zone::Repop: database.PopulateZoneSpawnList failed"); + + initgrids_timer.Start(); + + mod_repop(); +} + void Zone::Repop(uint32 delay) { if(!Depop()) diff --git a/zone/zone.h b/zone/zone.h index 94bba72e4..cbac77da9 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -140,6 +140,7 @@ public: bool Depop(bool StartSpawnTimer = false); void Repop(uint32 delay = 0); + void RepopClose(const glm::vec4& client_position, uint32 repop_distance); void ClearNPCTypeCache(int id); void SpawnStatus(Mob* client); void ShowEnabledSpawnStatus(Mob* client); diff --git a/zone/zonedb.h b/zone/zonedb.h index f49a9bf0d..66bb13e9e 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -359,6 +359,7 @@ public: bool LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list); bool LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list); bool PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay = 0); + bool PopulateZoneSpawnListClose(uint32 zoneid, LinkedList &spawn2_list, int16 version, const glm::vec4& client_position, uint32 repop_distance); Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, const glm::vec4& position, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); void UpdateRespawnTime(uint32 id, uint16 instance_id,uint32 timeleft);