diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index df2047ef9..d741ae81d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -199,6 +199,7 @@ SET(common_headers repositories/character_recipe_list_repository.h repositories/grid_repository.h repositories/grid_entries_repository.h + repositories/spawngroup_repository.h repositories/tradeskill_recipe_repository.h rdtsc.h rulesys.h diff --git a/common/repositories/spawngroup_repository.h b/common/repositories/spawngroup_repository.h new file mode 100644 index 000000000..4c800639f --- /dev/null +++ b/common/repositories/spawngroup_repository.h @@ -0,0 +1,164 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef EQEMU_SPAWNGROUP_REPOSITORY_H +#define EQEMU_SPAWNGROUP_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" + +class SpawnGroupRepository { +public: + struct SpawnGroup { + int id; + std::string name; + int8 spawn_limit; + int dist; + float max_x; + float min_x; + float max_y; + float min_y; + int delay; + int mindelay; + int despawn; + int despawn_timer; + int wp_spawns; + }; + + static std::vector Columns() + { + return { + "id", + "name", + "spawn_limit", + "dist", + "max_x", + "min_x", + "max_y", + "min_y", + "delay", + "mindelay", + "despawn", + "despawn_timer", + "wp_spawns", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string TableName() + { + return std::string("spawngroup"); + } + + static std::string BaseSelect() + { + return std::string( + fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ) + ); + } + + static SpawnGroup New() + { + SpawnGroup entry; + + entry.id = 0; + entry.name = ""; + entry.spawn_limit = 0; + entry.dist = 0; + entry.max_x = 0; + entry.min_x = 0; + entry.max_y = 0; + entry.min_y = 0; + entry.delay = 0; + entry.mindelay = 0; + entry.despawn = 0; + entry.despawn_timer = 0; + entry.wp_spawns = 0; + + return entry; + } + + static std::vector GetZoneSpawnGroups( + const std::string &zone_short_name, + int zone_version + ) + { + std::vector spawn_groups; + + auto results = content_db.QueryDatabase( + fmt::format( + SQL ( + {} INNER JOIN spawn2 ON spawn2.spawngroupID = spawngroup.id + WHERE spawn2.zone = '{}' and spawn2.version = {} + ), + BaseSelect(), + zone_short_name, + zone_version + ) + ); + + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnGroup entry{}; + + entry.id = atoi(row[0]); + entry.name = row[1]; + entry.spawn_limit = atoi(row[2]); + entry.dist = atof(row[3]); + entry.max_x = atof(row[4]); + entry.min_x = atof(row[5]); + entry.max_y = atof(row[6]); + entry.min_y = atof(row[7]); + entry.delay = atoi(row[8]); + entry.mindelay = atoi(row[9]); + entry.despawn = atoi(row[10]); + entry.despawn_timer = atoi(row[11]); + entry.wp_spawns = atoi(row[12]); + + spawn_groups.push_back(entry); + } + + return spawn_groups; + } + + static SpawnGroup GetGrid( + const std::vector &spawn_groups, + int spawn_group_id + ) + { + for (auto &row : spawn_groups) { + if (row.id == spawn_group_id) { + return row; + } + } + + return New(); + } + +}; + + +#endif //EQEMU_SPAWNGROUP_REPOSITORY_H diff --git a/zone/command.cpp b/zone/command.cpp index ef947b1b5..b7db3205e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -345,7 +345,6 @@ int command_init(void) command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) || command_add("reloadzps", "- Reload zone points from database", 150, 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) || @@ -5320,33 +5319,6 @@ void command_repop(Client *c, const Seperator *sep) zone->spawn2_timer.Trigger(); } -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(Chat::White, "Zone depop: Force resetting spawn timers."); - } - if (sep->IsNumber(1)) { - repop_distance = atoi(sep->arg[1]); - } - - c->Message(Chat::White, "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 d64950f9c..e1b028bb1 100644 --- a/zone/command.h +++ b/zone/command.h @@ -246,7 +246,6 @@ void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep); void command_repop(Client *c, const Seperator *sep); -void command_repopclose(Client *c, const Seperator *sep); void command_resetaa(Client* c,const Seperator *sep); void command_resetaa_timer(Client *c, const Seperator *sep); void command_revoke(Client *c, const Seperator *sep); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 74ec8d588..3571effe9 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -165,7 +165,10 @@ bool Spawn2::Process() { return (true); } - if (spawn_group == nullptr) { + /** + * Wait for init grids timer because we bulk load this data before trying to fetch it individually + */ + if (spawn_group == nullptr && zone->GetInitgridsTimer().Check()) { content_db.LoadSpawnGroupsByID(spawngroup_id_, &zone->spawn_group_list); spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); } @@ -916,7 +919,7 @@ void SpawnConditionManager::UpdateDBCondition(const char* zone_name, uint32 inst "(id, value, zone, instance_id) " "VALUES( %u, %u, '%s', %u)", cond_id, value, zone_name, instance_id); - content_db.QueryDatabase(query); + database.QueryDatabase(query); } bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std::string &zone_name) { @@ -1376,7 +1379,7 @@ int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instanc "WHERE zone = '%s' AND instance_id = %u AND id = %d", zone_short, instance_id, condition_id ); - auto results = content_db.QueryDatabase(query); + auto results = database.QueryDatabase(query); if (!results.Success()) { LogSpawns("Unable to query remote condition [{}] from zone [{}] in Get request", condition_id, zone_short); return 0; //dunno a better thing to do... diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index f33aa7fca..d1a83995f 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -325,6 +325,14 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn } for (auto row = results.begin(); row != results.end(); ++row) { + LogSpawnsDetail( + "[LoadSpawnGroupsByID] Loading spawn_group spawn_group_id [{}] name [{}] spawn_limit [{}] dist [{}]", + row[0], + row[1], + row[2], + row[3] + ); + auto new_spawn_group = new SpawnGroup( atoi(row[0]), row[1], @@ -375,6 +383,15 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn (row[4] ? atoi(row[4]) : 0) ); + LogSpawnsDetail( + "[LoadSpawnGroupsByID] Loading spawn_entry spawn_group_id [{}] npc_id [{}] chance [{}] condition_value_filter [{}] spawn_limit [{}]", + row[0], + row[1], + row[2], + row[3], + row[4] + ); + SpawnGroup *spawn_group = spawn_group_list->GetSpawnGroup(atoi(row[0])); if (!spawn_group) { safe_delete(new_spawn_entry); diff --git a/zone/zone.cpp b/zone/zone.cpp index 54e50f1d0..7d1266f9e 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1272,8 +1272,6 @@ bool Zone::Process() { EQEmu::InventoryProfile::CleanDirty(); - LogSpawns("Running Zone::Process -> Spawn2::Process"); - iterator.Reset(); while (iterator.MoreElements()) { if (iterator.GetData()->Process()) { @@ -1598,29 +1596,6 @@ 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 (!content_db.PopulateZoneSpawnListClose(zoneid, spawn2_list, GetInstanceVersion(), client_position, repop_distance)) - LogDebug("Error in Zone::Repop: database.PopulateZoneSpawnList failed"); - - initgrids_timer.Start(); - - mod_repop(); -} - void Zone::Repop(uint32 delay) { if (!Depop()) { @@ -1640,8 +1615,19 @@ void Zone::Repop(uint32 delay) quest_manager.ClearAllTimers(); - if (!content_db.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) + LogInfo("Loading spawn groups"); + if (!content_db.LoadSpawnGroups(short_name, GetInstanceVersion(), &spawn_group_list)) { + LogError("Loading spawn groups failed"); + } + + LogInfo("Loading spawn conditions"); + if (!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) { + LogError("Loading spawn conditions failed, continuing without them"); + } + + if (!content_db.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) { LogDebug("Error in Zone::Repop: database.PopulateZoneSpawnList failed"); + } LoadGrids(); @@ -2521,4 +2507,9 @@ void Zone::LoadGrids() { zone_grids = GridRepository::GetZoneGrids(GetZoneID()); zone_grid_entries = GridEntriesRepository::GetZoneGridEntries(GetZoneID()); -} \ No newline at end of file +} + +Timer Zone::GetInitgridsTimer() +{ + return initgrids_timer; +} diff --git a/zone/zone.h b/zone/zone.h index e0dbfe38e..bf986e6a1 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -259,7 +259,6 @@ public: void RemoveAuth(const char *iCharName, const char *iLSKey); void RemoveAuth(uint32 lsid); void Repop(uint32 delay = 0); - void RepopClose(const glm::vec4 &client_position, uint32 repop_distance); void RequestUCSServerStatus(); void ResetAuth(); void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute); @@ -285,6 +284,8 @@ public: ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f); ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f); + Timer GetInitgridsTimer(); + /** * GMSay Callback for LogSys * @@ -332,6 +333,7 @@ public: double GetMaxMovementUpdateRange() const { return max_movement_update_range; } + /** * Modding hooks */ @@ -352,8 +354,6 @@ private: bool staticzone; bool zone_has_current_time; bool quest_hot_reload_queued; - -private: double max_movement_update_range; char *long_name; char *map_name; @@ -384,7 +384,7 @@ private: Timer autoshutdown_timer; Timer clientauth_timer; Timer hotzone_timer; - Timer initgrids_timer; //delayed loading of initial grids. + Timer initgrids_timer; Timer qglobal_purge_timer; ZoneSpellsBlocked *blocked_spells;