From bcef171cf937eef3c654aec01ead62e9f1544076 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 03:55:53 -0500 Subject: [PATCH 01/20] Fix for tradeskill query [skip ci] --- zone/client_packet.cpp | 199 +++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 108 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 9085f1849..048a80cc3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -599,7 +599,7 @@ void Client::CompleteConnect() if (group) group->SendHPManaEndPacketsTo(this); } - + //bulk raid send in here eventually @@ -1237,7 +1237,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) database.ClearOldRecastTimestamps(cid); /* Clear out our old recast timestamps to keep the DB clean */ // set to full support in case they're a gm with items in disabled expansion slots..but, have their gm flag off... // item loss will occur when they use the 'empty' slots, if this is not done - m_inv.SetGMInventory(true); + m_inv.SetGMInventory(true); loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ @@ -1341,7 +1341,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) client_max_level = GetCharMaxLevelFromBucket(); } SetClientMaxLevel(client_max_level); - + // we know our class now, so we might have to fix our consume timer! if (class_ == MONK) consume_food_timer.SetTimer(CONSUMPTION_MNK_TIMER); @@ -2840,7 +2840,7 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) // rogue simply won't apply at all, no skill check done. uint16 poison_skill = GetSkill(EQEmu::skills::SkillApplyPoison); - + if (ChanceRoll < (.75 + poison_skill / 1000)) { ApplyPoisonSuccessResult = 1; AddProcToWeapon(poison->Proc.Effect, false, (GetDEX() / 100) + 103, POISON_PROC); @@ -3917,7 +3917,7 @@ void Client::Handle_OP_Bug(const EQApplicationPacket *app) Message(0, "Bug reporting is disabled on this server."); return; } - + if (app->size != sizeof(BugReport_Struct)) { printf("Wrong size of BugReport_Struct got %d expected %zu!\n", app->size, sizeof(BugReport_Struct)); } @@ -4362,9 +4362,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { sizeof(PlayerPositionUpdateClient_Struct), app->size); return; } - + PlayerPositionUpdateClient_Struct *ppu = (PlayerPositionUpdateClient_Struct *) app->pBuffer; - + /* Boat handling */ if (ppu->spawn_id != GetID()) { /* If player is controlling boat */ @@ -4374,16 +4374,16 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { controlling_boat_id = 0; return; } - + auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading)); boat->SetDelta(boat_delta); - + auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct *ppus = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer; boat->MakeSpawnUpdate(ppus); entity_list.QueueCloseClients(boat, outapp, true, 300, this, false); safe_delete(outapp); - + /* Update the boat's position on the server, without sending an update */ boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading), false); return; @@ -4398,9 +4398,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { if (cmob != nullptr) { cmob->SetPosition(ppu->x_pos, ppu->y_pos, ppu->z_pos); cmob->SetHeading(EQ12toFloat(ppu->heading)); - mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, + mMovementManager->SendCommandToClients(cmob, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny, nullptr, this); - cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, + cmob->CastToNPC()->SaveGuardSpot(glm::vec4(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading))); } } @@ -4418,7 +4418,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { // From this point forward, we need to use a new set of variables for client // position. If the client is in a boat, we need to add the boat pos and // the client offset together. - + float cx = ppu->x_pos; float cy = ppu->y_pos; float cz = ppu->z_pos; @@ -4443,45 +4443,45 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { /* Check to see if PPU should trigger an update to the rewind position. */ float rewind_x_diff = 0; float rewind_y_diff = 0; - + rewind_x_diff = cx - m_RewindLocation.x; rewind_x_diff *= rewind_x_diff; rewind_y_diff = cy - m_RewindLocation.y; rewind_y_diff *= rewind_y_diff; - - /* + + /* We only need to store updated values if the player has moved. If the player has moved more than units for x or y, then we'll store his pre-PPU x and y for /rewind, in case he gets stuck. */ - + if ((rewind_x_diff > 750) || (rewind_y_diff > 750)) m_RewindLocation = glm::vec3(m_Position); - + /* If the PPU was a large jump, such as a cross zone gate or Call of Hero, just update rewind coordinates to the new ppu coordinates. This will prevent exploitation. */ - + if ((rewind_x_diff > 5000) || (rewind_y_diff > 5000)) m_RewindLocation = glm::vec3(cx, cy, cz); - + if (proximity_timer.Check()) { entity_list.ProcessMove(this, glm::vec3(cx, cy, cz)); if (RuleB(TaskSystem, EnableTaskSystem) && RuleB(TaskSystem, EnableTaskProximity)) ProcessTaskProximities(cx, cy, cz); - + m_Proximity = glm::vec3(cx, cy, cz); } - + /* Update internal state */ m_Delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading)); - + if (IsTracking() && ((m_Position.x != cx) || (m_Position.y != cy))) { if (zone->random.Real(0, 100) < 70)//should be good CheckIncreaseSkill(EQEmu::skills::SkillTracking, nullptr, -20); } - + /* Break Hide if moving without sneaking and set rewind timer if moved */ if (cy != m_Position.y || cx != m_Position.x) { if ((hidden || improved_hidden) && !sneaking) { @@ -4500,7 +4500,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { } rewind_timer.Start(30000, true); } - + /* Handle client aggro scanning timers NPCs */ is_client_moving = (cy == m_Position.y && cx == m_Position.x) ? false : true; @@ -4564,55 +4564,55 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) { } int32 new_animation = ppu->animation; - + /* Update internal server position from what the client has sent */ m_Position.x = cx; m_Position.y = cy; m_Position.z = cz; - + /* Visual Debugging */ if (RuleB(Character, OPClientUpdateVisualDebug)) { LogDebug("ClientUpdate: ppu x: [{}] y: [{}] z: [{}] h: [{}]", cx, cy, cz, new_heading); this->SendAppearanceEffect(78, 0, 0, 0, 0); this->SendAppearanceEffect(41, 0, 0, 0, 0); } - + /* Only feed real time updates when client is moving */ if (is_client_moving || new_heading != m_Position.w || new_animation != animation) { - + animation = ppu->animation; m_Position.w = new_heading; - + /* Broadcast update to other clients */ auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); PlayerPositionUpdateServer_Struct *position_update = (PlayerPositionUpdateServer_Struct *) outapp->pBuffer; - + MakeSpawnUpdate(position_update); - + if (gm_hide_me) { entity_list.QueueClientsStatus(this, outapp, true, Admin(), 255); } else { entity_list.QueueCloseClients(this, outapp, true, RuleI(Range, ClientPositionUpdates), nullptr, true); } - - + + /* Always send position updates to group - send when beyond normal ClientPositionUpdate range */ Group *group = this->GetGroup(); Raid *raid = this->GetRaid(); - + if (raid) { raid->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1)); } else if (group) { group->QueueClients(this, outapp, true, true, (RuleI(Range, ClientPositionUpdates) * -1)); } - + safe_delete(outapp); } - + if (zone->watermap) { if (zone->watermap->InLiquid(glm::vec3(m_Position))) { CheckIncreaseSkill(EQEmu::skills::SkillSwimming, nullptr, -17); - + // Dismount horses when entering water if (GetHorseId() && RuleB(Character, DismountWater)) { SetHorseId(0); @@ -5749,23 +5749,23 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); else { FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - + std::vector points; Mob* target = entity_list.GetMob(t->npc_id); - + if (target == nullptr) { //empty length packet == not found. EQApplicationPacket outapp(OP_FindPersonReply, 0); QueuePacket(&outapp); return; } - + if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || target->CastToClient()->Buyer)) { Message(Chat::Yellow, "Moving you to Trader %s", target->GetName()); MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); } - + if (!RuleB(Pathing, Find) || !zone->pathing) { //fill in the path array... @@ -5788,40 +5788,40 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) { glm::vec3 Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); glm::vec3 End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - + bool partial = false; bool stuck = false; auto pathlist = zone->pathing->FindRoute(Start, End, partial, stuck); - + if (pathlist.empty() || partial) { EQApplicationPacket outapp(OP_FindPersonReply, 0); QueuePacket(&outapp); return; } - + // Live appears to send the points in this order: // Final destination. // Current Position. // rest of the points. FindPerson_Point p; - + int PointNumber = 0; - + bool LeadsToTeleporter = false; - + auto v = pathlist.back(); - + p.x = v.pos.x; p.y = v.pos.y; p.z = v.pos.z; points.push_back(p); - + p.x = GetX(); p.y = GetY(); p.z = GetZ(); points.push_back(p); - + for (auto Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) { if ((*Iterator).teleport) // Teleporter @@ -5829,7 +5829,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) LeadsToTeleporter = true; break; } - + glm::vec3 v = (*Iterator).pos; p.x = v.x; p.y = v.y; @@ -5837,17 +5837,17 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) points.push_back(p); ++PointNumber; } - + if (!LeadsToTeleporter) { p.x = target->GetX(); p.y = target->GetY(); p.z = target->GetZ(); - + points.push_back(p); } } - + SendPathPacket(points); } } @@ -11090,14 +11090,14 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) { case RaidCommandInviteIntoExisting: case RaidCommandInvite: { - + Client *player_to_invite = entity_list.GetClientByName(raid_command_packet->player_name); if (!player_to_invite) break; Group *player_to_invite_group = player_to_invite->GetGroup(); - + if (player_to_invite->HasRaid()) { Message(Chat::Red, "%s is already in a raid.", player_to_invite->GetName()); break; @@ -11112,7 +11112,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); break; } - + /* Send out invite to the client */ auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); RaidGeneral_Struct *raid_command = (RaidGeneral_Struct*)outapp->pBuffer; @@ -11124,7 +11124,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid_command->action = 20; player_to_invite->QueuePacket(outapp); - + safe_delete(outapp); break; @@ -11220,7 +11220,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } if (player_invited_group->IsLeader(player_invited_group->members[x])) { Client *c = nullptr; - + if (player_invited_group->members[x]->IsClient()) c = player_invited_group->members[x]->CastToClient(); else @@ -11230,24 +11230,24 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id, true, true, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } } else { Client *c = nullptr; - + if (player_invited_group->members[x]->IsClient()) c = player_invited_group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11281,12 +11281,12 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) c = group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id, false, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11294,17 +11294,17 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) else { Client *c = nullptr; - + if (group->members[x]->IsClient()) c = group->members[x]->CastToClient(); else continue; - + raid->SendRaidCreate(c); raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, raid_free_group_id); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11321,7 +11321,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) if (player_invited_group) { raid = new Raid(player_accepting_invite); - + entity_list.AddRaid(raid); raid->SetRaidDetails(); Client *addClientig = nullptr; @@ -11345,7 +11345,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, c); raid->AddMember(c, 0, true, true, true); raid->SendBulkRaid(c); - + if (raid->IsLocked()) { raid->SendRaidLockTo(c); } @@ -11470,7 +11470,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SetGroupLeader(raid_command_packet->leader_name, false); /* We were the leader of our old group */ - if (old_group < 12) { + if (old_group < 12) { /* Assign new group leader if we can */ for (int x = 0; x < MAX_RAID_MEMBERS; x++) { if (raid->members[x].GroupNumber == old_group) { @@ -11499,7 +11499,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) strn0cpy(raid_command_packet->playername, raid->members[x].membername, 64); worldserver.SendPacket(pack); - + safe_delete(pack); } break; @@ -11545,7 +11545,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) raid->SetGroupLeader(raid_command_packet->leader_name, false); for (int x = 0; x < MAX_RAID_MEMBERS; x++) { if (raid->members[x].GroupNumber == oldgrp && strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, raid_command_packet->leader_name) != 0){ - + raid->SetGroupLeader(raid->members[x].membername); raid->UpdateGroupAAs(oldgrp); @@ -11568,7 +11568,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) strn0cpy(raid_command->playername, raid->members[x].membername, 64); raid_command->zoneid = zone->GetZoneID(); raid_command->instance_id = zone->GetInstanceID(); - + worldserver.SendPacket(pack); safe_delete(pack); } @@ -11583,14 +11583,14 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) else { auto pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct* raid_command = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - + raid_command->rid = raid->GetID(); raid_command->zoneid = zone->GetZoneID(); raid_command->instance_id = zone->GetInstanceID(); strn0cpy(raid_command->playername, raid_command_packet->leader_name, 64); worldserver.SendPacket(pack); - + safe_delete(pack); } @@ -11778,7 +11778,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) { if (app->size != sizeof(TradeskillFavorites_Struct)) { LogError("Invalid size for TradeskillFavorites_Struct: Expected: [{}], Got: [{}]", - sizeof(TradeskillFavorites_Struct), app->size); + sizeof(TradeskillFavorites_Struct), app->size); return; } @@ -11828,6 +11828,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) if (first) //no favorites.... return; + // TODO: Clean this up const std::string query = StringFormat( SQL ( SELECT @@ -11844,11 +11845,11 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) AND tr.id IN (%s) AND tr.must_learn & 0x20 <> 0x20 AND ( - ( - tr.must_learn & 0x3 <> 0 - ) - OR (tr.must_learn & 0x3 = 0) - ) + ( + tr.must_learn & 0x3 <> 0 + ) + OR (tr.must_learn & 0x3 = 0) + ) GROUP BY tr.id HAVING @@ -11924,6 +11925,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) } //arbitrary limit of 200 recipes, makes sense to me. + // TODO: Clean this up std::string query = fmt::format( SQL( SELECT @@ -11931,37 +11933,19 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) tr.name, tr.trivial, SUM(tre.componentcount), - crl.madecount, tr.tradeskill FROM - tradeskill_recipe - AS tr - LEFT - JOIN - tradeskill_recipe_entries - AS - tre - ON - tr.id = tre.recipe_id - LEFT JOIN( - SELECT - recipe_id, - madecount - FROM - char_recipe_list - WHERE - char_id = {} - ) AS crl ON tr.id = crl.recipe_id + tradeskill_recipe AS tr + LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id WHERE - {} tr.trivial >= {} + {} tr.trivial >= {} AND tr.trivial <= {} AND tr.enabled <> 0 AND tr.must_learn & 0x20 <> 0x20 AND ( - ( - tr.must_learn & 0x3 <> 0 - AND crl.madecount IS NOT NULL - ) + ( + tr.must_learn & 0x3 <> 0 + ) OR (tr.must_learn & 0x3 = 0) ) GROUP BY @@ -11979,7 +11963,6 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) LIMIT 200 ), - CharacterID(), search_clause, p_recipes_search_struct->mintrivial, p_recipes_search_struct->maxtrivial, From cc8aa354f1a88220a0eeb9c4e6006120e7c775f5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 04:15:43 -0500 Subject: [PATCH 02/20] Add string_id define [skip ci] --- zone/string_ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/string_ids.h b/zone/string_ids.h index e543007a0..b7ee5ddd4 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -310,6 +310,7 @@ #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. #define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3' #define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. +#define ZONING_NO_EXPANSION 5052 //The zone that you are attempting to enter is part of an expansion that you do not yet own. You may need to return to the Login screen and enter an account key for that expansion. If you have received this message in error, please /petition or send an email to EQAccounts@soe.sony.com #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. #define ALREADY_IN_RAID 5060 //%1 is already in a raid. From c1e58673b2a989215b3fd906ff1955271bc072e9 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 05:36:18 -0500 Subject: [PATCH 03/20] Add expansion gating for zoning --- common/CMakeLists.txt | 2 +- common/content/world_content_service.h | 2 + common/repositories/zone_repository.h | 1054 ++++++++++++++++++++++++ world/world_server_command_handler.cpp | 19 +- world/world_server_command_handler.h | 1 + zone/main.cpp | 12 + zone/zoning.cpp | 32 +- 7 files changed, 1117 insertions(+), 5 deletions(-) create mode 100644 common/repositories/zone_repository.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 1eff58828..cfa404825 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -117,7 +117,7 @@ SET(repositories repositories/spawngroup_repository.h repositories/tradeskill_recipe_repository.h repositories/instance_list_repository.h - ) + repositories/zone_repository.h) SET(common_headers any.h diff --git a/common/content/world_content_service.h b/common/content/world_content_service.h index 850d536e2..11d1f03dc 100644 --- a/common/content/world_content_service.h +++ b/common/content/world_content_service.h @@ -62,8 +62,10 @@ namespace Expansion { static const char *ExpansionName[ExpansionNumber::MaxId] = { "Classic", "The Ruins of Kunark", + "The Shards of Velious", "The Shadows of Luclin", "The Planes of Power", + "The Legacy of Ykesha", "Lost Dungeons of Norrath", "Gates of Discord", "Omens of War", diff --git a/common/repositories/zone_repository.h b/common/repositories/zone_repository.h new file mode 100644 index 000000000..d4f29fa65 --- /dev/null +++ b/common/repositories/zone_repository.h @@ -0,0 +1,1054 @@ +/** + * 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_ZONE_REPOSITORY_H +#define EQEMU_ZONE_REPOSITORY_H + +#include "../database.h" +#include "../string_util.h" + +class ZoneRepository { +public: + struct Zone { + std::string short_name; + int id; + std::string file_name; + std::string long_name; + std::string map_file_name; + float safe_x; + float safe_y; + float safe_z; + float graveyard_id; + int8 min_level; + int8 min_status; + int zoneidnumber; + int8 version; + int timezone; + int maxclients; + int ruleset; + std::string note; + float underworld; + float minclip; + float maxclip; + float fog_minclip; + float fog_maxclip; + int8 fog_blue; + int8 fog_red; + int8 fog_green; + int8 sky; + int8 ztype; + float zone_exp_multiplier; + float walkspeed; + int8 time_type; + int8 fog_red1; + int8 fog_green1; + int8 fog_blue1; + float fog_minclip1; + float fog_maxclip1; + int8 fog_red2; + int8 fog_green2; + int8 fog_blue2; + float fog_minclip2; + float fog_maxclip2; + int8 fog_red3; + int8 fog_green3; + int8 fog_blue3; + float fog_minclip3; + float fog_maxclip3; + int8 fog_red4; + int8 fog_green4; + int8 fog_blue4; + float fog_minclip4; + float fog_maxclip4; + float fog_density; + std::string flag_needed; + int8 canbind; + int8 cancombat; + int8 canlevitate; + int8 castoutdoor; + int8 hotzone; + int8 insttype; + int shutdowndelay; + int8 peqzone; + int8 expansion; + int8 suspendbuffs; + int rain_chance1; + int rain_chance2; + int rain_chance3; + int rain_chance4; + int rain_duration1; + int rain_duration2; + int rain_duration3; + int rain_duration4; + int snow_chance1; + int snow_chance2; + int snow_chance3; + int snow_chance4; + int snow_duration1; + int snow_duration2; + int snow_duration3; + int snow_duration4; + float gravity; + int type; + int8 skylock; + int fast_regen_hp; + int fast_regen_mana; + int fast_regen_endurance; + int npc_max_aggro_dist; + int max_movement_update_range; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "short_name", + "id", + "file_name", + "long_name", + "map_file_name", + "safe_x", + "safe_y", + "safe_z", + "graveyard_id", + "min_level", + "min_status", + "zoneidnumber", + "version", + "timezone", + "maxclients", + "ruleset", + "note", + "underworld", + "minclip", + "maxclip", + "fog_minclip", + "fog_maxclip", + "fog_blue", + "fog_red", + "fog_green", + "sky", + "ztype", + "zone_exp_multiplier", + "walkspeed", + "time_type", + "fog_red1", + "fog_green1", + "fog_blue1", + "fog_minclip1", + "fog_maxclip1", + "fog_red2", + "fog_green2", + "fog_blue2", + "fog_minclip2", + "fog_maxclip2", + "fog_red3", + "fog_green3", + "fog_blue3", + "fog_minclip3", + "fog_maxclip3", + "fog_red4", + "fog_green4", + "fog_blue4", + "fog_minclip4", + "fog_maxclip4", + "fog_density", + "flag_needed", + "canbind", + "cancombat", + "canlevitate", + "castoutdoor", + "hotzone", + "insttype", + "shutdowndelay", + "peqzone", + "expansion", + "suspendbuffs", + "rain_chance1", + "rain_chance2", + "rain_chance3", + "rain_chance4", + "rain_duration1", + "rain_duration2", + "rain_duration3", + "rain_duration4", + "snow_chance1", + "snow_chance2", + "snow_chance3", + "snow_chance4", + "snow_duration1", + "snow_duration2", + "snow_duration3", + "snow_duration4", + "gravity", + "type", + "skylock", + "fast_regen_hp", + "fast_regen_mana", + "fast_regen_endurance", + "npc_max_aggro_dist", + "max_movement_update_range", + }; + } + + static std::string ColumnsRaw() + { + return std::string(implode(", ", Columns())); + } + + static std::string InsertColumnsRaw() + { + std::vector insert_columns; + + for (auto &column : Columns()) { + if (column == PrimaryKey()) { + continue; + } + + insert_columns.push_back(column); + } + + return std::string(implode(", ", insert_columns)); + } + + static std::string TableName() + { + return std::string("zone"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + ColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + InsertColumnsRaw() + ); + } + + static Zone NewEntity() + { + Zone entry{}; + + entry.short_name = ""; + entry.id = 0; + entry.file_name = ""; + entry.long_name = ""; + entry.map_file_name = ""; + entry.safe_x = 0; + entry.safe_y = 0; + entry.safe_z = 0; + entry.graveyard_id = 0; + entry.min_level = 0; + entry.min_status = 0; + entry.zoneidnumber = 0; + entry.version = 0; + entry.timezone = 0; + entry.maxclients = 0; + entry.ruleset = 0; + entry.note = ""; + entry.underworld = 0; + entry.minclip = 450; + entry.maxclip = 450; + entry.fog_minclip = 450; + entry.fog_maxclip = 450; + entry.fog_blue = 0; + entry.fog_red = 0; + entry.fog_green = 0; + entry.sky = 1; + entry.ztype = 1; + entry.zone_exp_multiplier = 0.00; + entry.walkspeed = 0.4; + entry.time_type = 2; + entry.fog_red1 = 0; + entry.fog_green1 = 0; + entry.fog_blue1 = 0; + entry.fog_minclip1 = 450; + entry.fog_maxclip1 = 450; + entry.fog_red2 = 0; + entry.fog_green2 = 0; + entry.fog_blue2 = 0; + entry.fog_minclip2 = 450; + entry.fog_maxclip2 = 450; + entry.fog_red3 = 0; + entry.fog_green3 = 0; + entry.fog_blue3 = 0; + entry.fog_minclip3 = 450; + entry.fog_maxclip3 = 450; + entry.fog_red4 = 0; + entry.fog_green4 = 0; + entry.fog_blue4 = 0; + entry.fog_minclip4 = 450; + entry.fog_maxclip4 = 450; + entry.fog_density = 0; + entry.flag_needed = ""; + entry.canbind = 1; + entry.cancombat = 1; + entry.canlevitate = 1; + entry.castoutdoor = 1; + entry.hotzone = 0; + entry.insttype = 0; + entry.shutdowndelay = 5000; + entry.peqzone = 1; + entry.expansion = 0; + entry.suspendbuffs = 0; + entry.rain_chance1 = 0; + entry.rain_chance2 = 0; + entry.rain_chance3 = 0; + entry.rain_chance4 = 0; + entry.rain_duration1 = 0; + entry.rain_duration2 = 0; + entry.rain_duration3 = 0; + entry.rain_duration4 = 0; + entry.snow_chance1 = 0; + entry.snow_chance2 = 0; + entry.snow_chance3 = 0; + entry.snow_chance4 = 0; + entry.snow_duration1 = 0; + entry.snow_duration2 = 0; + entry.snow_duration3 = 0; + entry.snow_duration4 = 0; + entry.gravity = 0.4; + entry.type = 0; + entry.skylock = 0; + entry.fast_regen_hp = 180; + entry.fast_regen_mana = 180; + entry.fast_regen_endurance = 180; + entry.npc_max_aggro_dist = 600; + entry.max_movement_update_range = 600; + + return entry; + } + + static Zone GetZoneEntry( + const std::vector &zones, + int zone_id + ) + { + for (auto &zone : zones) { + if (zone.id == zone_id) { + return zone; + } + } + + return NewEntity(); + } + + static Zone FindOne( + int zone_id + ) + { + auto results = content_db.QueryDatabase( + fmt::format( + "{} WHERE id = {} LIMIT 1", + BaseSelect(), + zone_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + Zone entry{}; + + entry.short_name = row[0] ? row[0] : ""; + entry.id = atoi(row[1]); + entry.file_name = row[2] ? row[2] : ""; + entry.long_name = row[3] ? row[3] : ""; + entry.map_file_name = row[4] ? row[4] : ""; + entry.safe_x = atof(row[5]); + entry.safe_y = atof(row[6]); + entry.safe_z = atof(row[7]); + entry.graveyard_id = atof(row[8]); + entry.min_level = atoi(row[9]); + entry.min_status = atoi(row[10]); + entry.zoneidnumber = atoi(row[11]); + entry.version = atoi(row[12]); + entry.timezone = atoi(row[13]); + entry.maxclients = atoi(row[14]); + entry.ruleset = atoi(row[15]); + entry.note = row[16] ? row[16] : ""; + entry.underworld = atof(row[17]); + entry.minclip = atof(row[18]); + entry.maxclip = atof(row[19]); + entry.fog_minclip = atof(row[20]); + entry.fog_maxclip = atof(row[21]); + entry.fog_blue = atoi(row[22]); + entry.fog_red = atoi(row[23]); + entry.fog_green = atoi(row[24]); + entry.sky = atoi(row[25]); + entry.ztype = atoi(row[26]); + entry.zone_exp_multiplier = atof(row[27]); + entry.walkspeed = atof(row[28]); + entry.time_type = atoi(row[29]); + entry.fog_red1 = atoi(row[30]); + entry.fog_green1 = atoi(row[31]); + entry.fog_blue1 = atoi(row[32]); + entry.fog_minclip1 = atof(row[33]); + entry.fog_maxclip1 = atof(row[34]); + entry.fog_red2 = atoi(row[35]); + entry.fog_green2 = atoi(row[36]); + entry.fog_blue2 = atoi(row[37]); + entry.fog_minclip2 = atof(row[38]); + entry.fog_maxclip2 = atof(row[39]); + entry.fog_red3 = atoi(row[40]); + entry.fog_green3 = atoi(row[41]); + entry.fog_blue3 = atoi(row[42]); + entry.fog_minclip3 = atof(row[43]); + entry.fog_maxclip3 = atof(row[44]); + entry.fog_red4 = atoi(row[45]); + entry.fog_green4 = atoi(row[46]); + entry.fog_blue4 = atoi(row[47]); + entry.fog_minclip4 = atof(row[48]); + entry.fog_maxclip4 = atof(row[49]); + entry.fog_density = atof(row[50]); + entry.flag_needed = row[51] ? row[51] : ""; + entry.canbind = atoi(row[52]); + entry.cancombat = atoi(row[53]); + entry.canlevitate = atoi(row[54]); + entry.castoutdoor = atoi(row[55]); + entry.hotzone = atoi(row[56]); + entry.insttype = atoi(row[57]); + entry.shutdowndelay = atoi(row[58]); + entry.peqzone = atoi(row[59]); + entry.expansion = atoi(row[60]); + entry.suspendbuffs = atoi(row[61]); + entry.rain_chance1 = atoi(row[62]); + entry.rain_chance2 = atoi(row[63]); + entry.rain_chance3 = atoi(row[64]); + entry.rain_chance4 = atoi(row[65]); + entry.rain_duration1 = atoi(row[66]); + entry.rain_duration2 = atoi(row[67]); + entry.rain_duration3 = atoi(row[68]); + entry.rain_duration4 = atoi(row[69]); + entry.snow_chance1 = atoi(row[70]); + entry.snow_chance2 = atoi(row[71]); + entry.snow_chance3 = atoi(row[72]); + entry.snow_chance4 = atoi(row[73]); + entry.snow_duration1 = atoi(row[74]); + entry.snow_duration2 = atoi(row[75]); + entry.snow_duration3 = atoi(row[76]); + entry.snow_duration4 = atoi(row[77]); + entry.gravity = atof(row[78]); + entry.type = atoi(row[79]); + entry.skylock = atoi(row[80]); + entry.fast_regen_hp = atoi(row[81]); + entry.fast_regen_mana = atoi(row[82]); + entry.fast_regen_endurance = atoi(row[83]); + entry.npc_max_aggro_dist = atoi(row[84]); + entry.max_movement_update_range = atoi(row[85]); + + return entry; + } + + return NewEntity(); + } + + static int DeleteOne( + int zone_id + ) + { + auto results = content_db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + zone_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Zone zone_entry + ) + { + std::vector update_values; + + auto columns = Columns(); + + update_values.push_back(columns[0] + " = '" + EscapeString(zone_entry.short_name) + "'"); + update_values.push_back(columns[2] + " = '" + EscapeString(zone_entry.file_name) + "'"); + update_values.push_back(columns[3] + " = '" + EscapeString(zone_entry.long_name) + "'"); + update_values.push_back(columns[4] + " = '" + EscapeString(zone_entry.map_file_name) + "'"); + update_values.push_back(columns[5] + " = " + std::to_string(zone_entry.safe_x)); + update_values.push_back(columns[6] + " = " + std::to_string(zone_entry.safe_y)); + update_values.push_back(columns[7] + " = " + std::to_string(zone_entry.safe_z)); + update_values.push_back(columns[8] + " = " + std::to_string(zone_entry.graveyard_id)); + update_values.push_back(columns[9] + " = " + std::to_string(zone_entry.min_level)); + update_values.push_back(columns[10] + " = " + std::to_string(zone_entry.min_status)); + update_values.push_back(columns[11] + " = " + std::to_string(zone_entry.zoneidnumber)); + update_values.push_back(columns[12] + " = " + std::to_string(zone_entry.version)); + update_values.push_back(columns[13] + " = " + std::to_string(zone_entry.timezone)); + update_values.push_back(columns[14] + " = " + std::to_string(zone_entry.maxclients)); + update_values.push_back(columns[15] + " = " + std::to_string(zone_entry.ruleset)); + update_values.push_back(columns[16] + " = '" + EscapeString(zone_entry.note) + "'"); + update_values.push_back(columns[17] + " = " + std::to_string(zone_entry.underworld)); + update_values.push_back(columns[18] + " = " + std::to_string(zone_entry.minclip)); + update_values.push_back(columns[19] + " = " + std::to_string(zone_entry.maxclip)); + update_values.push_back(columns[20] + " = " + std::to_string(zone_entry.fog_minclip)); + update_values.push_back(columns[21] + " = " + std::to_string(zone_entry.fog_maxclip)); + update_values.push_back(columns[22] + " = " + std::to_string(zone_entry.fog_blue)); + update_values.push_back(columns[23] + " = " + std::to_string(zone_entry.fog_red)); + update_values.push_back(columns[24] + " = " + std::to_string(zone_entry.fog_green)); + update_values.push_back(columns[25] + " = " + std::to_string(zone_entry.sky)); + update_values.push_back(columns[26] + " = " + std::to_string(zone_entry.ztype)); + update_values.push_back(columns[27] + " = " + std::to_string(zone_entry.zone_exp_multiplier)); + update_values.push_back(columns[28] + " = " + std::to_string(zone_entry.walkspeed)); + update_values.push_back(columns[29] + " = " + std::to_string(zone_entry.time_type)); + update_values.push_back(columns[30] + " = " + std::to_string(zone_entry.fog_red1)); + update_values.push_back(columns[31] + " = " + std::to_string(zone_entry.fog_green1)); + update_values.push_back(columns[32] + " = " + std::to_string(zone_entry.fog_blue1)); + update_values.push_back(columns[33] + " = " + std::to_string(zone_entry.fog_minclip1)); + update_values.push_back(columns[34] + " = " + std::to_string(zone_entry.fog_maxclip1)); + update_values.push_back(columns[35] + " = " + std::to_string(zone_entry.fog_red2)); + update_values.push_back(columns[36] + " = " + std::to_string(zone_entry.fog_green2)); + update_values.push_back(columns[37] + " = " + std::to_string(zone_entry.fog_blue2)); + update_values.push_back(columns[38] + " = " + std::to_string(zone_entry.fog_minclip2)); + update_values.push_back(columns[39] + " = " + std::to_string(zone_entry.fog_maxclip2)); + update_values.push_back(columns[40] + " = " + std::to_string(zone_entry.fog_red3)); + update_values.push_back(columns[41] + " = " + std::to_string(zone_entry.fog_green3)); + update_values.push_back(columns[42] + " = " + std::to_string(zone_entry.fog_blue3)); + update_values.push_back(columns[43] + " = " + std::to_string(zone_entry.fog_minclip3)); + update_values.push_back(columns[44] + " = " + std::to_string(zone_entry.fog_maxclip3)); + update_values.push_back(columns[45] + " = " + std::to_string(zone_entry.fog_red4)); + update_values.push_back(columns[46] + " = " + std::to_string(zone_entry.fog_green4)); + update_values.push_back(columns[47] + " = " + std::to_string(zone_entry.fog_blue4)); + update_values.push_back(columns[48] + " = " + std::to_string(zone_entry.fog_minclip4)); + update_values.push_back(columns[49] + " = " + std::to_string(zone_entry.fog_maxclip4)); + update_values.push_back(columns[50] + " = " + std::to_string(zone_entry.fog_density)); + update_values.push_back(columns[51] + " = '" + EscapeString(zone_entry.flag_needed) + "'"); + update_values.push_back(columns[52] + " = " + std::to_string(zone_entry.canbind)); + update_values.push_back(columns[53] + " = " + std::to_string(zone_entry.cancombat)); + update_values.push_back(columns[54] + " = " + std::to_string(zone_entry.canlevitate)); + update_values.push_back(columns[55] + " = " + std::to_string(zone_entry.castoutdoor)); + update_values.push_back(columns[56] + " = " + std::to_string(zone_entry.hotzone)); + update_values.push_back(columns[57] + " = " + std::to_string(zone_entry.insttype)); + update_values.push_back(columns[58] + " = " + std::to_string(zone_entry.shutdowndelay)); + update_values.push_back(columns[59] + " = " + std::to_string(zone_entry.peqzone)); + update_values.push_back(columns[60] + " = " + std::to_string(zone_entry.expansion)); + update_values.push_back(columns[61] + " = " + std::to_string(zone_entry.suspendbuffs)); + update_values.push_back(columns[62] + " = " + std::to_string(zone_entry.rain_chance1)); + update_values.push_back(columns[63] + " = " + std::to_string(zone_entry.rain_chance2)); + update_values.push_back(columns[64] + " = " + std::to_string(zone_entry.rain_chance3)); + update_values.push_back(columns[65] + " = " + std::to_string(zone_entry.rain_chance4)); + update_values.push_back(columns[66] + " = " + std::to_string(zone_entry.rain_duration1)); + update_values.push_back(columns[67] + " = " + std::to_string(zone_entry.rain_duration2)); + update_values.push_back(columns[68] + " = " + std::to_string(zone_entry.rain_duration3)); + update_values.push_back(columns[69] + " = " + std::to_string(zone_entry.rain_duration4)); + update_values.push_back(columns[70] + " = " + std::to_string(zone_entry.snow_chance1)); + update_values.push_back(columns[71] + " = " + std::to_string(zone_entry.snow_chance2)); + update_values.push_back(columns[72] + " = " + std::to_string(zone_entry.snow_chance3)); + update_values.push_back(columns[73] + " = " + std::to_string(zone_entry.snow_chance4)); + update_values.push_back(columns[74] + " = " + std::to_string(zone_entry.snow_duration1)); + update_values.push_back(columns[75] + " = " + std::to_string(zone_entry.snow_duration2)); + update_values.push_back(columns[76] + " = " + std::to_string(zone_entry.snow_duration3)); + update_values.push_back(columns[77] + " = " + std::to_string(zone_entry.snow_duration4)); + update_values.push_back(columns[78] + " = " + std::to_string(zone_entry.gravity)); + update_values.push_back(columns[79] + " = " + std::to_string(zone_entry.type)); + update_values.push_back(columns[80] + " = " + std::to_string(zone_entry.skylock)); + update_values.push_back(columns[81] + " = " + std::to_string(zone_entry.fast_regen_hp)); + update_values.push_back(columns[82] + " = " + std::to_string(zone_entry.fast_regen_mana)); + update_values.push_back(columns[83] + " = " + std::to_string(zone_entry.fast_regen_endurance)); + update_values.push_back(columns[84] + " = " + std::to_string(zone_entry.npc_max_aggro_dist)); + update_values.push_back(columns[85] + " = " + std::to_string(zone_entry.max_movement_update_range)); + + auto results = content_db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + implode(", ", update_values), + PrimaryKey(), + zone_entry.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static Zone InsertOne( + Zone zone_entry + ) + { + std::vector insert_values; + + insert_values.push_back("'" + EscapeString(zone_entry.short_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.file_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.long_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.map_file_name) + "'"); + insert_values.push_back(std::to_string(zone_entry.safe_x)); + insert_values.push_back(std::to_string(zone_entry.safe_y)); + insert_values.push_back(std::to_string(zone_entry.safe_z)); + insert_values.push_back(std::to_string(zone_entry.graveyard_id)); + insert_values.push_back(std::to_string(zone_entry.min_level)); + insert_values.push_back(std::to_string(zone_entry.min_status)); + insert_values.push_back(std::to_string(zone_entry.zoneidnumber)); + insert_values.push_back(std::to_string(zone_entry.version)); + insert_values.push_back(std::to_string(zone_entry.timezone)); + insert_values.push_back(std::to_string(zone_entry.maxclients)); + insert_values.push_back(std::to_string(zone_entry.ruleset)); + insert_values.push_back("'" + EscapeString(zone_entry.note) + "'"); + insert_values.push_back(std::to_string(zone_entry.underworld)); + insert_values.push_back(std::to_string(zone_entry.minclip)); + insert_values.push_back(std::to_string(zone_entry.maxclip)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip)); + insert_values.push_back(std::to_string(zone_entry.fog_blue)); + insert_values.push_back(std::to_string(zone_entry.fog_red)); + insert_values.push_back(std::to_string(zone_entry.fog_green)); + insert_values.push_back(std::to_string(zone_entry.sky)); + insert_values.push_back(std::to_string(zone_entry.ztype)); + insert_values.push_back(std::to_string(zone_entry.zone_exp_multiplier)); + insert_values.push_back(std::to_string(zone_entry.walkspeed)); + insert_values.push_back(std::to_string(zone_entry.time_type)); + insert_values.push_back(std::to_string(zone_entry.fog_red1)); + insert_values.push_back(std::to_string(zone_entry.fog_green1)); + insert_values.push_back(std::to_string(zone_entry.fog_blue1)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip1)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip1)); + insert_values.push_back(std::to_string(zone_entry.fog_red2)); + insert_values.push_back(std::to_string(zone_entry.fog_green2)); + insert_values.push_back(std::to_string(zone_entry.fog_blue2)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip2)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip2)); + insert_values.push_back(std::to_string(zone_entry.fog_red3)); + insert_values.push_back(std::to_string(zone_entry.fog_green3)); + insert_values.push_back(std::to_string(zone_entry.fog_blue3)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip3)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip3)); + insert_values.push_back(std::to_string(zone_entry.fog_red4)); + insert_values.push_back(std::to_string(zone_entry.fog_green4)); + insert_values.push_back(std::to_string(zone_entry.fog_blue4)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip4)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip4)); + insert_values.push_back(std::to_string(zone_entry.fog_density)); + insert_values.push_back("'" + EscapeString(zone_entry.flag_needed) + "'"); + insert_values.push_back(std::to_string(zone_entry.canbind)); + insert_values.push_back(std::to_string(zone_entry.cancombat)); + insert_values.push_back(std::to_string(zone_entry.canlevitate)); + insert_values.push_back(std::to_string(zone_entry.castoutdoor)); + insert_values.push_back(std::to_string(zone_entry.hotzone)); + insert_values.push_back(std::to_string(zone_entry.insttype)); + insert_values.push_back(std::to_string(zone_entry.shutdowndelay)); + insert_values.push_back(std::to_string(zone_entry.peqzone)); + insert_values.push_back(std::to_string(zone_entry.expansion)); + insert_values.push_back(std::to_string(zone_entry.suspendbuffs)); + insert_values.push_back(std::to_string(zone_entry.rain_chance1)); + insert_values.push_back(std::to_string(zone_entry.rain_chance2)); + insert_values.push_back(std::to_string(zone_entry.rain_chance3)); + insert_values.push_back(std::to_string(zone_entry.rain_chance4)); + insert_values.push_back(std::to_string(zone_entry.rain_duration1)); + insert_values.push_back(std::to_string(zone_entry.rain_duration2)); + insert_values.push_back(std::to_string(zone_entry.rain_duration3)); + insert_values.push_back(std::to_string(zone_entry.rain_duration4)); + insert_values.push_back(std::to_string(zone_entry.snow_chance1)); + insert_values.push_back(std::to_string(zone_entry.snow_chance2)); + insert_values.push_back(std::to_string(zone_entry.snow_chance3)); + insert_values.push_back(std::to_string(zone_entry.snow_chance4)); + insert_values.push_back(std::to_string(zone_entry.snow_duration1)); + insert_values.push_back(std::to_string(zone_entry.snow_duration2)); + insert_values.push_back(std::to_string(zone_entry.snow_duration3)); + insert_values.push_back(std::to_string(zone_entry.snow_duration4)); + insert_values.push_back(std::to_string(zone_entry.gravity)); + insert_values.push_back(std::to_string(zone_entry.type)); + insert_values.push_back(std::to_string(zone_entry.skylock)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_hp)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_mana)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_endurance)); + insert_values.push_back(std::to_string(zone_entry.npc_max_aggro_dist)); + insert_values.push_back(std::to_string(zone_entry.max_movement_update_range)); + + auto results = content_db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + implode(",", insert_values) + ) + ); + + if (results.Success()) { + zone_entry.id = results.LastInsertedID(); + return zone_entry; + } + + zone_entry = ZoneRepository::NewEntity(); + + return zone_entry; + } + + static int InsertMany( + std::vector zone_entries + ) + { + std::vector insert_chunks; + + for (auto &zone_entry: zone_entries) { + std::vector insert_values; + + insert_values.push_back("'" + EscapeString(zone_entry.short_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.file_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.long_name) + "'"); + insert_values.push_back("'" + EscapeString(zone_entry.map_file_name) + "'"); + insert_values.push_back(std::to_string(zone_entry.safe_x)); + insert_values.push_back(std::to_string(zone_entry.safe_y)); + insert_values.push_back(std::to_string(zone_entry.safe_z)); + insert_values.push_back(std::to_string(zone_entry.graveyard_id)); + insert_values.push_back(std::to_string(zone_entry.min_level)); + insert_values.push_back(std::to_string(zone_entry.min_status)); + insert_values.push_back(std::to_string(zone_entry.zoneidnumber)); + insert_values.push_back(std::to_string(zone_entry.version)); + insert_values.push_back(std::to_string(zone_entry.timezone)); + insert_values.push_back(std::to_string(zone_entry.maxclients)); + insert_values.push_back(std::to_string(zone_entry.ruleset)); + insert_values.push_back("'" + EscapeString(zone_entry.note) + "'"); + insert_values.push_back(std::to_string(zone_entry.underworld)); + insert_values.push_back(std::to_string(zone_entry.minclip)); + insert_values.push_back(std::to_string(zone_entry.maxclip)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip)); + insert_values.push_back(std::to_string(zone_entry.fog_blue)); + insert_values.push_back(std::to_string(zone_entry.fog_red)); + insert_values.push_back(std::to_string(zone_entry.fog_green)); + insert_values.push_back(std::to_string(zone_entry.sky)); + insert_values.push_back(std::to_string(zone_entry.ztype)); + insert_values.push_back(std::to_string(zone_entry.zone_exp_multiplier)); + insert_values.push_back(std::to_string(zone_entry.walkspeed)); + insert_values.push_back(std::to_string(zone_entry.time_type)); + insert_values.push_back(std::to_string(zone_entry.fog_red1)); + insert_values.push_back(std::to_string(zone_entry.fog_green1)); + insert_values.push_back(std::to_string(zone_entry.fog_blue1)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip1)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip1)); + insert_values.push_back(std::to_string(zone_entry.fog_red2)); + insert_values.push_back(std::to_string(zone_entry.fog_green2)); + insert_values.push_back(std::to_string(zone_entry.fog_blue2)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip2)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip2)); + insert_values.push_back(std::to_string(zone_entry.fog_red3)); + insert_values.push_back(std::to_string(zone_entry.fog_green3)); + insert_values.push_back(std::to_string(zone_entry.fog_blue3)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip3)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip3)); + insert_values.push_back(std::to_string(zone_entry.fog_red4)); + insert_values.push_back(std::to_string(zone_entry.fog_green4)); + insert_values.push_back(std::to_string(zone_entry.fog_blue4)); + insert_values.push_back(std::to_string(zone_entry.fog_minclip4)); + insert_values.push_back(std::to_string(zone_entry.fog_maxclip4)); + insert_values.push_back(std::to_string(zone_entry.fog_density)); + insert_values.push_back("'" + EscapeString(zone_entry.flag_needed) + "'"); + insert_values.push_back(std::to_string(zone_entry.canbind)); + insert_values.push_back(std::to_string(zone_entry.cancombat)); + insert_values.push_back(std::to_string(zone_entry.canlevitate)); + insert_values.push_back(std::to_string(zone_entry.castoutdoor)); + insert_values.push_back(std::to_string(zone_entry.hotzone)); + insert_values.push_back(std::to_string(zone_entry.insttype)); + insert_values.push_back(std::to_string(zone_entry.shutdowndelay)); + insert_values.push_back(std::to_string(zone_entry.peqzone)); + insert_values.push_back(std::to_string(zone_entry.expansion)); + insert_values.push_back(std::to_string(zone_entry.suspendbuffs)); + insert_values.push_back(std::to_string(zone_entry.rain_chance1)); + insert_values.push_back(std::to_string(zone_entry.rain_chance2)); + insert_values.push_back(std::to_string(zone_entry.rain_chance3)); + insert_values.push_back(std::to_string(zone_entry.rain_chance4)); + insert_values.push_back(std::to_string(zone_entry.rain_duration1)); + insert_values.push_back(std::to_string(zone_entry.rain_duration2)); + insert_values.push_back(std::to_string(zone_entry.rain_duration3)); + insert_values.push_back(std::to_string(zone_entry.rain_duration4)); + insert_values.push_back(std::to_string(zone_entry.snow_chance1)); + insert_values.push_back(std::to_string(zone_entry.snow_chance2)); + insert_values.push_back(std::to_string(zone_entry.snow_chance3)); + insert_values.push_back(std::to_string(zone_entry.snow_chance4)); + insert_values.push_back(std::to_string(zone_entry.snow_duration1)); + insert_values.push_back(std::to_string(zone_entry.snow_duration2)); + insert_values.push_back(std::to_string(zone_entry.snow_duration3)); + insert_values.push_back(std::to_string(zone_entry.snow_duration4)); + insert_values.push_back(std::to_string(zone_entry.gravity)); + insert_values.push_back(std::to_string(zone_entry.type)); + insert_values.push_back(std::to_string(zone_entry.skylock)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_hp)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_mana)); + insert_values.push_back(std::to_string(zone_entry.fast_regen_endurance)); + insert_values.push_back(std::to_string(zone_entry.npc_max_aggro_dist)); + insert_values.push_back(std::to_string(zone_entry.max_movement_update_range)); + + insert_chunks.push_back("(" + implode(",", insert_values) + ")"); + } + + std::vector insert_values; + + auto results = content_db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All() + { + std::vector all_entries; + + auto results = content_db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + Zone entry{}; + + entry.short_name = row[0] ? row[0] : ""; + entry.id = atoi(row[1]); + entry.file_name = row[2] ? row[2] : ""; + entry.long_name = row[3] ? row[3] : ""; + entry.map_file_name = row[4] ? row[4] : ""; + entry.safe_x = atof(row[5]); + entry.safe_y = atof(row[6]); + entry.safe_z = atof(row[7]); + entry.graveyard_id = atof(row[8]); + entry.min_level = atoi(row[9]); + entry.min_status = atoi(row[10]); + entry.zoneidnumber = atoi(row[11]); + entry.version = atoi(row[12]); + entry.timezone = atoi(row[13]); + entry.maxclients = atoi(row[14]); + entry.ruleset = atoi(row[15]); + entry.note = row[16] ? row[16] : ""; + entry.underworld = atof(row[17]); + entry.minclip = atof(row[18]); + entry.maxclip = atof(row[19]); + entry.fog_minclip = atof(row[20]); + entry.fog_maxclip = atof(row[21]); + entry.fog_blue = atoi(row[22]); + entry.fog_red = atoi(row[23]); + entry.fog_green = atoi(row[24]); + entry.sky = atoi(row[25]); + entry.ztype = atoi(row[26]); + entry.zone_exp_multiplier = atof(row[27]); + entry.walkspeed = atof(row[28]); + entry.time_type = atoi(row[29]); + entry.fog_red1 = atoi(row[30]); + entry.fog_green1 = atoi(row[31]); + entry.fog_blue1 = atoi(row[32]); + entry.fog_minclip1 = atof(row[33]); + entry.fog_maxclip1 = atof(row[34]); + entry.fog_red2 = atoi(row[35]); + entry.fog_green2 = atoi(row[36]); + entry.fog_blue2 = atoi(row[37]); + entry.fog_minclip2 = atof(row[38]); + entry.fog_maxclip2 = atof(row[39]); + entry.fog_red3 = atoi(row[40]); + entry.fog_green3 = atoi(row[41]); + entry.fog_blue3 = atoi(row[42]); + entry.fog_minclip3 = atof(row[43]); + entry.fog_maxclip3 = atof(row[44]); + entry.fog_red4 = atoi(row[45]); + entry.fog_green4 = atoi(row[46]); + entry.fog_blue4 = atoi(row[47]); + entry.fog_minclip4 = atof(row[48]); + entry.fog_maxclip4 = atof(row[49]); + entry.fog_density = atof(row[50]); + entry.flag_needed = row[51] ? row[51] : ""; + entry.canbind = atoi(row[52]); + entry.cancombat = atoi(row[53]); + entry.canlevitate = atoi(row[54]); + entry.castoutdoor = atoi(row[55]); + entry.hotzone = atoi(row[56]); + entry.insttype = atoi(row[57]); + entry.shutdowndelay = atoi(row[58]); + entry.peqzone = atoi(row[59]); + entry.expansion = atoi(row[60]); + entry.suspendbuffs = atoi(row[61]); + entry.rain_chance1 = atoi(row[62]); + entry.rain_chance2 = atoi(row[63]); + entry.rain_chance3 = atoi(row[64]); + entry.rain_chance4 = atoi(row[65]); + entry.rain_duration1 = atoi(row[66]); + entry.rain_duration2 = atoi(row[67]); + entry.rain_duration3 = atoi(row[68]); + entry.rain_duration4 = atoi(row[69]); + entry.snow_chance1 = atoi(row[70]); + entry.snow_chance2 = atoi(row[71]); + entry.snow_chance3 = atoi(row[72]); + entry.snow_chance4 = atoi(row[73]); + entry.snow_duration1 = atoi(row[74]); + entry.snow_duration2 = atoi(row[75]); + entry.snow_duration3 = atoi(row[76]); + entry.snow_duration4 = atoi(row[77]); + entry.gravity = atof(row[78]); + entry.type = atoi(row[79]); + entry.skylock = atoi(row[80]); + entry.fast_regen_hp = atoi(row[81]); + entry.fast_regen_mana = atoi(row[82]); + entry.fast_regen_endurance = atoi(row[83]); + entry.npc_max_aggro_dist = atoi(row[84]); + entry.max_movement_update_range = atoi(row[85]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static std::vector GetWhere(std::string where_filter) + { + std::vector all_entries; + + auto results = content_db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + Zone entry{}; + + entry.short_name = row[0] ? row[0] : ""; + entry.id = atoi(row[1]); + entry.file_name = row[2] ? row[2] : ""; + entry.long_name = row[3] ? row[3] : ""; + entry.map_file_name = row[4] ? row[4] : ""; + entry.safe_x = atof(row[5]); + entry.safe_y = atof(row[6]); + entry.safe_z = atof(row[7]); + entry.graveyard_id = atof(row[8]); + entry.min_level = atoi(row[9]); + entry.min_status = atoi(row[10]); + entry.zoneidnumber = atoi(row[11]); + entry.version = atoi(row[12]); + entry.timezone = atoi(row[13]); + entry.maxclients = atoi(row[14]); + entry.ruleset = atoi(row[15]); + entry.note = row[16] ? row[16] : ""; + entry.underworld = atof(row[17]); + entry.minclip = atof(row[18]); + entry.maxclip = atof(row[19]); + entry.fog_minclip = atof(row[20]); + entry.fog_maxclip = atof(row[21]); + entry.fog_blue = atoi(row[22]); + entry.fog_red = atoi(row[23]); + entry.fog_green = atoi(row[24]); + entry.sky = atoi(row[25]); + entry.ztype = atoi(row[26]); + entry.zone_exp_multiplier = atof(row[27]); + entry.walkspeed = atof(row[28]); + entry.time_type = atoi(row[29]); + entry.fog_red1 = atoi(row[30]); + entry.fog_green1 = atoi(row[31]); + entry.fog_blue1 = atoi(row[32]); + entry.fog_minclip1 = atof(row[33]); + entry.fog_maxclip1 = atof(row[34]); + entry.fog_red2 = atoi(row[35]); + entry.fog_green2 = atoi(row[36]); + entry.fog_blue2 = atoi(row[37]); + entry.fog_minclip2 = atof(row[38]); + entry.fog_maxclip2 = atof(row[39]); + entry.fog_red3 = atoi(row[40]); + entry.fog_green3 = atoi(row[41]); + entry.fog_blue3 = atoi(row[42]); + entry.fog_minclip3 = atof(row[43]); + entry.fog_maxclip3 = atof(row[44]); + entry.fog_red4 = atoi(row[45]); + entry.fog_green4 = atoi(row[46]); + entry.fog_blue4 = atoi(row[47]); + entry.fog_minclip4 = atof(row[48]); + entry.fog_maxclip4 = atof(row[49]); + entry.fog_density = atof(row[50]); + entry.flag_needed = row[51] ? row[51] : ""; + entry.canbind = atoi(row[52]); + entry.cancombat = atoi(row[53]); + entry.canlevitate = atoi(row[54]); + entry.castoutdoor = atoi(row[55]); + entry.hotzone = atoi(row[56]); + entry.insttype = atoi(row[57]); + entry.shutdowndelay = atoi(row[58]); + entry.peqzone = atoi(row[59]); + entry.expansion = atoi(row[60]); + entry.suspendbuffs = atoi(row[61]); + entry.rain_chance1 = atoi(row[62]); + entry.rain_chance2 = atoi(row[63]); + entry.rain_chance3 = atoi(row[64]); + entry.rain_chance4 = atoi(row[65]); + entry.rain_duration1 = atoi(row[66]); + entry.rain_duration2 = atoi(row[67]); + entry.rain_duration3 = atoi(row[68]); + entry.rain_duration4 = atoi(row[69]); + entry.snow_chance1 = atoi(row[70]); + entry.snow_chance2 = atoi(row[71]); + entry.snow_chance3 = atoi(row[72]); + entry.snow_chance4 = atoi(row[73]); + entry.snow_duration1 = atoi(row[74]); + entry.snow_duration2 = atoi(row[75]); + entry.snow_duration3 = atoi(row[76]); + entry.snow_duration4 = atoi(row[77]); + entry.gravity = atof(row[78]); + entry.type = atoi(row[79]); + entry.skylock = atoi(row[80]); + entry.fast_regen_hp = atoi(row[81]); + entry.fast_regen_mana = atoi(row[82]); + entry.fast_regen_endurance = atoi(row[83]); + entry.npc_max_aggro_dist = atoi(row[84]); + entry.max_movement_update_range = atoi(row[85]); + + all_entries.push_back(entry); + } + + return all_entries; + } + + static int DeleteWhere(std::string where_filter) + { + auto results = content_db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + PrimaryKey(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + +}; + +#endif //EQEMU_ZONE_REPOSITORY_H diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 0c7eb0ac1..33fc10345 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -58,6 +58,7 @@ namespace WorldserverCommandHandler { function_map["database:schema"] = &WorldserverCommandHandler::DatabaseGetSchema; function_map["database:dump"] = &WorldserverCommandHandler::DatabaseDump; function_map["test:test"] = &WorldserverCommandHandler::TestCommand; + function_map["test:expansion"] = &WorldserverCommandHandler::ExpansionTestCommand; function_map["test:repository"] = &WorldserverCommandHandler::TestRepository; EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); @@ -291,6 +292,22 @@ namespace WorldserverCommandHandler { return; } + } + + /** + * @param argc + * @param argv + * @param cmd + * @param description + */ + void ExpansionTestCommand(int argc, char **argv, argh::parser &cmd, std::string &description) + { + description = "Expansion test command"; + + if (cmd[{"-h", "--help"}]) { + return; + } + if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { LogInfo("No rule set configured, using default rules"); } @@ -405,4 +422,4 @@ namespace WorldserverCommandHandler { } -} \ No newline at end of file +} diff --git a/world/world_server_command_handler.h b/world/world_server_command_handler.h index 66f81a130..85baf7f90 100644 --- a/world/world_server_command_handler.h +++ b/world/world_server_command_handler.h @@ -32,6 +32,7 @@ namespace WorldserverCommandHandler { void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description); void DatabaseDump(int argc, char **argv, argh::parser &cmd, std::string &description); void TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description); + void ExpansionTestCommand(int argc, char **argv, argh::parser &cmd, std::string &description); void TestRepository(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/zone/main.cpp b/zone/main.cpp index c7c03a33a..3f8026cad 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -90,6 +90,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #else #include #include "../common/unix.h" + +#include "../common/content/world_content_service.h" + #endif volatile bool RunLoops = true; @@ -107,6 +110,7 @@ TaskManager *taskmanager = 0; NpcScaleManager *npc_scale_manager; QuestParserCollection *parse = 0; EQEmuLogSys LogSys; +WorldContentService content_service; const SPDat_Spell_Struct* spells; int32 SPDAT_RECORDS = -1; const ZoneConfig *Config; @@ -392,6 +396,14 @@ int main(int argc, char** argv) { LogInfo("Initialized dynamic dictionary entries"); } + content_service.SetCurrentExpansion(RuleI(Expansion, CurrentExpansion)); + + LogInfo( + "Current expansion is [{}] ({})", + content_service.GetCurrentExpansion(), + Expansion::ExpansionName[content_service.GetCurrentExpansion()] + ); + #ifdef BOTS LogInfo("Loading bot commands"); int botretval = bot_command_init(); diff --git a/zone/zoning.cpp b/zone/zoning.cpp index eb5bc6805..36a2ee5a2 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -35,6 +35,9 @@ extern QueryServ* QServ; extern WorldServer worldserver; extern Zone* zone; +#include "../common/repositories/zone_repository.h" +#include "../common/content/world_content_service.h" + void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { #ifdef BOTS @@ -284,6 +287,29 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { //TODO: ADVENTURE ENTRANCE CHECK + /** + * Expansion check + */ + auto zones = ZoneRepository::GetWhere( + fmt::format( + "expansion <= {} AND short_name = '{}'", + (content_service.GetCurrentExpansion() + 1), + target_zone_name + ) + ); + + LogInfo( + "Checking zone request [{}] for expansion [{}] ({}) success [{}]", + target_zone_name, + (content_service.GetCurrentExpansion() + 1), + Expansion::ExpansionName[content_service.GetCurrentExpansion()], + !zones.empty() ? "true" : "false" + ); + + if (zones.empty()) { + myerror = ZONE_ERROR_NOEXPANSION; + } + if(myerror == 1) { //we have successfully zoned DoZoneSuccess(zc, target_zone_id, target_instance_id, dest_x, dest_y, dest_z, dest_h, ignorerestrictions); @@ -530,7 +556,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z heading = m_pp.binds[0].heading; zonesummon_ignorerestrictions = 1; - LogDebug("Player [{}] has died and will be zoned to bind point in zone: [{}] at LOC x=[{}], y=[{}], z=[{}], heading=[{}]", + LogDebug("Player [{}] has died and will be zoned to bind point in zone: [{}] at LOC x=[{}], y=[{}], z=[{}], heading=[{}]", GetName(), pZoneName, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, m_pp.binds[0].heading); break; case SummonPC: @@ -539,8 +565,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z SetHeading(heading); break; case Rewind: - LogDebug("[{}] has requested a /rewind from [{}], [{}], [{}], to [{}], [{}], [{}] in [{}]", GetName(), - m_Position.x, m_Position.y, m_Position.z, + LogDebug("[{}] has requested a /rewind from [{}], [{}], [{}], to [{}], [{}], [{}] in [{}]", GetName(), + m_Position.x, m_Position.y, m_Position.z, m_RewindLocation.x, m_RewindLocation.y, m_RewindLocation.z, zone->GetShortName()); m_ZoneSummonLocation = glm::vec3(x, y, z); m_Position = glm::vec4(m_ZoneSummonLocation, 0.0f); From 8a711f3c64d9cd4ed3c69fc3f7c5589aa2abb5fc Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 05:51:21 -0500 Subject: [PATCH 04/20] Expansion filtering for doors --- .../criteria/content_filter_criteria.h | 15 ++++++++++----- zone/doors.cpp | 5 ++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/common/repositories/criteria/content_filter_criteria.h b/common/repositories/criteria/content_filter_criteria.h index c316bd87f..1173415c5 100644 --- a/common/repositories/criteria/content_filter_criteria.h +++ b/common/repositories/criteria/content_filter_criteria.h @@ -31,23 +31,28 @@ namespace ContentFilterCriteria { std::string criteria; criteria += fmt::format( - " AND (min_expansion >= {} OR min_expansion = 0)", + " AND (min_expansion <= {} OR min_expansion = 0)", content_service.GetCurrentExpansion() ); criteria += fmt::format( - " AND (max_expansion <= {} OR max_expansion = 0)", + " AND (max_expansion >= {} OR max_expansion = 0)", content_service.GetCurrentExpansion() ); std::vector flags = content_service.GetContentFlags(); - for (auto &flag: flags) { + for (auto &flag: flags) { flag = "'" + flag + "'"; } + std::string flags_in_filter; + if (!flags.empty()) { + flags_in_filter = fmt::format("OR content_flags IN ({})", implode(", ", flags)); + } + criteria += fmt::format( - " AND (content_flags IS NULL OR content_flags IN ({}))", - implode(", ", flags) + " AND (content_flags IS NULL {})", + flags_in_filter ); return std::string(criteria); diff --git a/zone/doors.cpp b/zone/doors.cpp index 6cb0617fd..82521aac2 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -28,6 +28,7 @@ #include "string_ids.h" #include "worldserver.h" #include "zonedb.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include #include @@ -716,10 +717,12 @@ bool ZoneDatabase::LoadDoors(int32 door_count, Door *into, const char *zone_name " WHERE " " zone = '%s' " " AND ( version = % u OR version = - 1 ) " + " %s " " ORDER BY " " doorid ASC ", zone_name, - version + version, + ContentFilterCriteria::apply().c_str() ); auto results = QueryDatabase(query); if (!results.Success()) { From 80baf1b9ed3d2b8737695b41e2a54ac5dae0a368 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 05:59:20 -0500 Subject: [PATCH 05/20] Filter objects by expansion --- zone/zone.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 7d1266f9e..f18c1db76 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -56,6 +56,7 @@ #include "npc_scale_manager.h" #include "../common/data_verification.h" #include "zone_reload.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include #include @@ -140,7 +141,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { if(iInstanceID != 0) { auto pack = new ServerPacket(ServerOP_AdventureZoneData, sizeof(uint16)); - *((uint16*)pack->pBuffer) = iInstanceID; + *((uint16*)pack->pBuffer) = iInstanceID; worldserver.SendPacket(pack); delete pack; } @@ -170,11 +171,14 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { //this really loads the objects into entity_list bool Zone::LoadZoneObjects() { - std::string query = - StringFormat("SELECT id, zoneid, xpos, ypos, zpos, heading, itemid, charges, objectname, type, icon, " - "unknown08, unknown10, unknown20, unknown24, unknown76, size, tilt_x, tilt_y, display_name " - "FROM object WHERE zoneid = %i AND (version = %u OR version = -1)", - zoneid, instanceversion); + std::string query = StringFormat( + "SELECT id, zoneid, xpos, ypos, zpos, heading, itemid, charges, objectname, type, icon, " + "unknown08, unknown10, unknown20, unknown24, unknown76, size, tilt_x, tilt_y, display_name " + "FROM object WHERE zoneid = %i AND (version = %u OR version = -1) %s", + zoneid, + instanceversion, + ContentFilterCriteria::apply().c_str() + ); auto results = content_db.QueryDatabase(query); if (!results.Success()) { LogError("Error Loading Objects from DB: [{}]", @@ -523,7 +527,7 @@ void Zone::LoadNewMerchantData(uint32 merchantid) { void Zone::GetMerchantDataForZoneLoad() { LogInfo("Loading Merchant Lists"); - std::string query = StringFormat( + std::string query = StringFormat( "SELECT " "DISTINCT ml.merchantid, " "ml.slot, " @@ -847,7 +851,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) { LogDebug("Graveyard ID is [{}]", graveyard_id()); bool GraveYardLoaded = content_db.GetZoneGraveyard(graveyard_id(), &pgraveyard_zoneid, &m_Graveyard.x, &m_Graveyard.y, &m_Graveyard.z, &m_Graveyard.w); - + if (GraveYardLoaded) { LogDebug("Loaded a graveyard for zone [{}]: graveyard zoneid is [{}] at [{}]", short_name, graveyard_zoneid(), to_string(m_Graveyard).c_str()); } From be8a6b6af410a29464c8c4c7114b42ccf3d5a43f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 06:08:56 -0500 Subject: [PATCH 06/20] Add spawn expansion filtering --- common/repositories/criteria/content_filter_criteria.h | 4 ++-- zone/spawngroup.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/common/repositories/criteria/content_filter_criteria.h b/common/repositories/criteria/content_filter_criteria.h index 1173415c5..9a86dcb99 100644 --- a/common/repositories/criteria/content_filter_criteria.h +++ b/common/repositories/criteria/content_filter_criteria.h @@ -47,11 +47,11 @@ namespace ContentFilterCriteria { std::string flags_in_filter; if (!flags.empty()) { - flags_in_filter = fmt::format("OR content_flags IN ({})", implode(", ", flags)); + flags_in_filter = fmt::format(" OR content_flags IN ({})", implode(", ", flags)); } criteria += fmt::format( - " AND (content_flags IS NULL {})", + " AND (content_flags IS NULL{})", flags_in_filter ); diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index d1a83995f..49aeea35b 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -24,6 +24,7 @@ #include "spawngroup.h" #include "zone.h" #include "zonedb.h" +#include "../common/repositories/criteria/content_filter_criteria.h" extern EntityList entity_list; extern Zone *zone; @@ -209,9 +210,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG spawn2.spawngroupID = spawngroup.ID AND spawn2.version = {} and zone = '{}' + {} ), version, - zone_name + zone_name, + ContentFilterCriteria::apply() ); auto results = QueryDatabase(query); From e17bd303b8992c21f406df20fc525ba2d02feb5e Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 18:00:48 -0500 Subject: [PATCH 07/20] Fix typos to stop triggering Secrets [skip ci] --- common/content/world_content_service.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/content/world_content_service.h b/common/content/world_content_service.h index 11d1f03dc..9bb568207 100644 --- a/common/content/world_content_service.h +++ b/common/content/world_content_service.h @@ -28,7 +28,7 @@ namespace Expansion { enum ExpansionNumber { Classic = 0, TheRuinsOfKunark, - TheShardsOfVelious, + TheScarsOfVelious, TheShadowsOfLuclin, ThePlanesOfPower, TheLegacyOfYkesha, @@ -62,7 +62,7 @@ namespace Expansion { static const char *ExpansionName[ExpansionNumber::MaxId] = { "Classic", "The Ruins of Kunark", - "The Shards of Velious", + "The Scars of Velious", "The Shadows of Luclin", "The Planes of Power", "The Legacy of Ykesha", From 17e9198a58e48e0df5565ed53f020f84900802e2 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sat, 4 Apr 2020 21:38:05 -0500 Subject: [PATCH 08/20] Another typo fix [skip ci] --- common/content/world_content_service.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/content/world_content_service.h b/common/content/world_content_service.h index 9bb568207..d6d6723a9 100644 --- a/common/content/world_content_service.h +++ b/common/content/world_content_service.h @@ -98,7 +98,7 @@ public: bool IsClassicEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::Classic; } bool IsTheRuinsOfKunarkEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheRuinsOfKunark; } - bool IsTheShardsOfVeliousEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheShardsOfVelious; } + bool IsTheScarsOfVeliousEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheScarsOfVelious; } bool IsTheShadowsOfLuclinEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheShadowsOfLuclin; } bool IsThePlanesOfPowerEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::ThePlanesOfPower; } bool IsTheLegacyOfYkeshaEnabled() { return GetCurrentExpansion() >= Expansion::ExpansionNumber::TheLegacyOfYkesha; } @@ -126,7 +126,7 @@ public: bool IsCurrentExpansionClassic() { return current_expansion == Expansion::ExpansionNumber::Classic; } bool IsCurrentExpansionTheRuinsOfKunark() { return current_expansion == Expansion::ExpansionNumber::TheRuinsOfKunark; } - bool IsCurrentExpansionTheShardsOfVelious() { return current_expansion == Expansion::ExpansionNumber::TheShardsOfVelious; } + bool IsCurrentExpansionTheScarsOfVelious() { return current_expansion == Expansion::ExpansionNumber::TheScarsOfVelious; } bool IsCurrentExpansionTheShadowsOfLuclin() { return current_expansion == Expansion::ExpansionNumber::TheShadowsOfLuclin; } bool IsCurrentExpansionThePlanesOfPower() { return current_expansion == Expansion::ExpansionNumber::ThePlanesOfPower; } bool IsCurrentExpansionTheLegacyOfYkesha() { return current_expansion == Expansion::ExpansionNumber::TheLegacyOfYkesha; } From 9a90bdf91aef474720c2e915b320600cba3078f5 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 18:40:19 -0500 Subject: [PATCH 09/20] Filter tradeskills [skip ci] --- world/world_server_command_handler.cpp | 2 +- zone/client_packet.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 33fc10345..ec832271e 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -325,7 +325,7 @@ namespace WorldserverCommandHandler { "Current expansion is [{}] ({}) is Velious Enabled [{}] Criteria [{}]", content_service.GetCurrentExpansion(), Expansion::ExpansionName[content_service.GetCurrentExpansion()], - content_service.IsTheShardsOfVeliousEnabled() ? "true" : "false", + content_service.IsTheScarsOfVeliousEnabled() ? "true" : "false", ContentFilterCriteria::apply() ); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 048a80cc3..f4a53ad8b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -61,6 +61,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "worldserver.h" #include "zone.h" #include "mob_movement_manager.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #ifdef BOTS #include "bot.h" @@ -11850,6 +11851,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) ) OR (tr.must_learn & 0x3 = 0) ) + %s GROUP BY tr.id HAVING @@ -11862,10 +11864,12 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) ) ) > 0 AND SUM(tre.componentcount) <= %u + LIMIT 100 ), favoriteIDs.c_str(), + ContentFilterCriteria::apply().c_str(), containers.c_str(), combineObjectSlots ); @@ -11948,6 +11952,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) ) OR (tr.must_learn & 0x3 = 0) ) + {} GROUP BY tr.id HAVING @@ -11960,12 +11965,14 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) ) ) > 0 AND SUM(tre.componentcount) <= {} + LIMIT 200 ), search_clause, p_recipes_search_struct->mintrivial, p_recipes_search_struct->maxtrivial, + ContentFilterCriteria::apply(), containers_where_clause, combine_object_slots ); From 6e3922b7ccbd001e477352013b16fe9e152ea840 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 18:50:30 -0500 Subject: [PATCH 10/20] Filter merchants [skip ci] --- zone/zone.cpp | 149 +++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index f18c1db76..5358d9a22 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -502,73 +502,111 @@ void Zone::LoadTempMerchantData() void Zone::LoadNewMerchantData(uint32 merchantid) { std::list merlist; - std::string query = StringFormat("SELECT item, slot, faction_required, level_required, alt_currency_cost, " - "classes_required, probability FROM merchantlist WHERE merchantid=%d ORDER BY slot", merchantid); + + std::string query = fmt::format( + SQL( + SELECT + item, + slot, + faction_required, + level_required, + alt_currency_cost, + classes_required, + probability + FROM + merchantlist + WHERE + merchantid = {} + {} + ORDER BY + slot + ), + merchantid, + ContentFilterCriteria::apply() + ); + auto results = content_db.QueryDatabase(query); if (!results.Success()) { return; - } + } - for(auto row = results.begin(); row != results.end(); ++row) { - MerchantList ml; - ml.id = merchantid; - ml.item = atoul(row[0]); - ml.slot = atoul(row[1]); - ml.faction_required = atoul(row[2]); - ml.level_required = atoul(row[3]); - ml.alt_currency_cost = atoul(row[4]); - ml.classes_required = atoul(row[5]); - ml.probability = atoul(row[6]); - merlist.push_back(ml); - } + for (auto row = results.begin(); row != results.end(); ++row) { + MerchantList ml; + ml.id = merchantid; + ml.item = atoul(row[0]); + ml.slot = atoul(row[1]); + ml.faction_required = atoul(row[2]); + ml.level_required = atoul(row[3]); + ml.alt_currency_cost = atoul(row[4]); + ml.classes_required = atoul(row[5]); + ml.probability = atoul(row[6]); + merlist.push_back(ml); + } - merchanttable[merchantid] = merlist; + merchanttable[merchantid] = merlist; } void Zone::GetMerchantDataForZoneLoad() { LogInfo("Loading Merchant Lists"); - std::string query = StringFormat( - "SELECT " - "DISTINCT ml.merchantid, " - "ml.slot, " - "ml.item, " - "ml.faction_required, " - "ml.level_required, " - "ml.alt_currency_cost, " - "ml.classes_required, " - "ml.probability " - "FROM " - "merchantlist AS ml, " - "npc_types AS nt, " - "spawnentry AS se, " - "spawn2 AS s2 " - "WHERE nt.merchant_id = ml.merchantid AND nt.id = se.npcid " - "AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i " - "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); + std::string query = fmt::format( + SQL ( + SELECT + DISTINCT ml.merchantid, + ml.slot, + ml.item, + ml.faction_required, + ml.level_required, + ml.alt_currency_cost, + ml.classes_required, + ml.probability + FROM + merchantlist AS ml, + npc_types AS nt, + spawnentry AS se, + spawn2 AS s2 + WHERE + nt.merchant_id = ml.merchantid + AND nt.id = se.npcid + AND se.spawngroupid = s2.spawngroupid + AND s2.zone = '{}' + AND s2.version = {} + {} + ORDER BY + ml.slot + ), + GetShortName(), + GetInstanceVersion(), + ContentFilterCriteria::apply() + ); + auto results = content_db.QueryDatabase(query); - std::map >::iterator cur; - uint32 npcid = 0; + + std::map >::iterator merchant_list; + + uint32 npc_id = 0; + if (results.RowCount() == 0) { LogDebug("No Merchant Data found for [{}]", GetShortName()); return; } for (auto row = results.begin(); row != results.end(); ++row) { - MerchantList ml; - ml.id = atoul(row[0]); - if (npcid != ml.id) { - cur = merchanttable.find(ml.id); - if (cur == merchanttable.end()) { + MerchantList merchant_list_entry{}; + merchant_list_entry.id = atoul(row[0]); + if (npc_id != merchant_list_entry.id) { + merchant_list = merchanttable.find(merchant_list_entry.id); + if (merchant_list == merchanttable.end()) { std::list empty; - merchanttable[ml.id] = empty; - cur = merchanttable.find(ml.id); + merchanttable[merchant_list_entry.id] = empty; + merchant_list = merchanttable.find(merchant_list_entry.id); } - npcid = ml.id; + + npc_id = merchant_list_entry.id; } - auto iter = cur->second.begin(); + auto iter = merchant_list->second.begin(); bool found = false; - while (iter != cur->second.end()) { - if ((*iter).item == ml.id) { + while (iter != merchant_list->second.end()) { + if ((*iter).item == merchant_list_entry.id) { found = true; break; } @@ -579,14 +617,15 @@ void Zone::GetMerchantDataForZoneLoad() { continue; } - ml.slot = atoul(row[1]); - ml.item = atoul(row[2]); - ml.faction_required = atoul(row[3]); - ml.level_required = atoul(row[4]); - ml.alt_currency_cost = atoul(row[5]); - ml.classes_required = atoul(row[6]); - ml.probability = atoul(row[7]); - cur->second.push_back(ml); + merchant_list_entry.slot = atoul(row[1]); + merchant_list_entry.item = atoul(row[2]); + merchant_list_entry.faction_required = atoul(row[3]); + merchant_list_entry.level_required = atoul(row[4]); + merchant_list_entry.alt_currency_cost = atoul(row[5]); + merchant_list_entry.classes_required = atoul(row[6]); + merchant_list_entry.probability = atoul(row[7]); + + merchant_list->second.push_back(merchant_list_entry); } } From 152d985821a2bcc3c1abc6f6767ff8967f408894 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:01:45 -0500 Subject: [PATCH 11/20] Filter loot tables and drops [skip ci] --- client_files/export/main.cpp | 2 + client_files/import/main.cpp | 8 +- common/shareddb.cpp | 140 ++++++++++++++++++++++++----------- shared_memory/main.cpp | 35 +++++++++ 4 files changed, 140 insertions(+), 45 deletions(-) diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index a6f3c3bec..fd8b19026 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -26,8 +26,10 @@ #include "../../common/crash.h" #include "../../common/rulesys.h" #include "../../common/string_util.h" +#include "../../common/content/world_content_service.h" EQEmuLogSys LogSys; +WorldContentService content_service; void ExportSpells(SharedDatabase *db); void ExportSkillCaps(SharedDatabase *db); diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 3c25fa618..64b2aee41 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -24,8 +24,10 @@ #include "../../common/crash.h" #include "../../common/rulesys.h" #include "../../common/string_util.h" +#include "../../common/content/world_content_service.h" EQEmuLogSys LogSys; +WorldContentService content_service; void ImportSpells(SharedDatabase *db); void ImportSkillCaps(SharedDatabase *db); @@ -87,7 +89,7 @@ int main(int argc, char **argv) { ImportDBStrings(&database); LogSys.CloseFileLogs(); - + return 0; } @@ -324,10 +326,10 @@ void ImportDBStrings(SharedDatabase *db) { std::string sql; int id, type; std::string value; - + id = atoi(split[0].c_str()); type = atoi(split[1].c_str()); - + if(split.size() >= 3) { value = ::EscapeString(split[2]); } diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 48cc1309d..358a80d00 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -38,6 +38,7 @@ #include "shareddb.h" #include "string_util.h" #include "eqemu_config.h" +#include "repositories/criteria/content_filter_criteria.h" namespace ItemField { @@ -567,7 +568,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) { if (!char_id || !inv) return false; - + // Retrieve character inventory std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, " @@ -728,7 +729,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv) char_id, item_id, slot_id); } } - + if (cv_conflict) { char char_name[64] = ""; GetCharName(char_id, char_name); @@ -1462,7 +1463,7 @@ bool SharedDatabase::GetCommandSettings(std::mapSharedMemDir + prefix + std::string("spells"); spells_mmf = std::unique_ptr(new EQEmu::MemoryMappedFile(file_name)); *records = *reinterpret_cast(spells_mmf->Get()); @@ -1996,7 +1997,11 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot loot_table_count = 0; max_loot_table = 0; loot_table_entries = 0; - const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; + const std::string query = + fmt::format( + "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable WHERE TRUE {}", + ContentFilterCriteria::apply() + ); auto results = QueryDatabase(query); if (!results.Success()) { return; @@ -2017,7 +2022,11 @@ void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_d max_loot_drop = 0; loot_drop_entries = 0; - const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; + const std::string query = fmt::format( + "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop WHERE TRUE {}", + ContentFilterCriteria::apply() + ); + auto results = QueryDatabase(query); if (!results.Success()) { return; @@ -2039,50 +2048,79 @@ void SharedDatabase::LoadLootTables(void *data, uint32 size) { uint8 loot_table[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)]; LootTable_Struct *lt = reinterpret_cast(loot_table); - const std::string query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin, " - "loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " - "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries " - "ON loottable.id = loottable_entries.loottable_id ORDER BY id"; + const std::string query = fmt::format( + SQL( + SELECT + loottable.id, + loottable.mincash, + loottable.maxcash, + loottable.avgcoin, + loottable_entries.lootdrop_id, + loottable_entries.multiplier, + loottable_entries.droplimit, + loottable_entries.mindrop, + loottable_entries.probability + FROM + loottable + LEFT JOIN loottable_entries ON loottable.id = loottable_entries.loottable_id + WHERE TRUE {} + ORDER BY + id + ), + ContentFilterCriteria::apply() + ); + auto results = QueryDatabase(query); if (!results.Success()) { return; } - uint32 current_id = 0; - uint32 current_entry = 0; + uint32 current_id = 0; + uint32 current_entry = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if (id != current_id) { + if (current_id != 0) { + hash.insert( + current_id, + loot_table, + (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); + } - memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); - current_entry = 0; - current_id = id; - lt->mincash = static_cast(atoul(row[1])); - lt->maxcash = static_cast(atoul(row[2])); - lt->avgcoin = static_cast(atoul(row[3])); - } + memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); + current_entry = 0; + current_id = id; + lt->mincash = static_cast(atoul(row[1])); + lt->maxcash = static_cast(atoul(row[2])); + lt->avgcoin = static_cast(atoul(row[3])); + } - if(current_entry > 128) - continue; + if (current_entry > 128) { + continue; + } - if(!row[4]) - continue; + if (!row[4]) { + continue; + } - lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); - lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); - lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); - lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); - lt->Entries[current_entry].probability = static_cast(atof(row[8])); + lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); + lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); + lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); + lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); + lt->Entries[current_entry].probability = static_cast(atof(row[8])); - ++(lt->NumEntries); - ++current_entry; - } + ++(lt->NumEntries); + ++current_entry; + } - if(current_id != 0) - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); + if (current_id != 0) { + hash.insert( + current_id, + loot_table, + (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries)) + ); + } } @@ -2091,11 +2129,29 @@ void SharedDatabase::LoadLootDrops(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; LootDrop_Struct *ld = reinterpret_cast(loot_drop); + + const std::string query = fmt::format( + SQL( + SELECT + lootdrop.id, + lootdrop_entries.item_id, + lootdrop_entries.item_charges, + lootdrop_entries.equip_item, + lootdrop_entries.chance, + lootdrop_entries.minlevel, + lootdrop_entries.maxlevel, + lootdrop_entries.multiplier + FROM + lootdrop + JOIN lootdrop_entries ON lootdrop.id = lootdrop_entries.lootdrop_id + WHERE + TRUE {} + ORDER BY + lootdrop_id + ), + ContentFilterCriteria::apply() + ); - const std::string query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " - "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " - "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " - "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; auto results = QueryDatabase(query); if (!results.Success()) { return; diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index a515903e1..cd516017d 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -33,8 +33,10 @@ #include "skill_caps.h" #include "spells.h" #include "base_data.h" +#include "../common/content/world_content_service.h" EQEmuLogSys LogSys; +WorldContentService content_service; #ifdef _WINDOWS #include @@ -139,6 +141,39 @@ int main(int argc, char **argv) } } + /** + * Rules: TODO: Remove later + */ + { + std::string tmp; + if (database.GetVariable("RuleSet", tmp)) { + LogInfo("Loading rule set [{}]", tmp.c_str()); + if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) { + LogError("Failed to load ruleset [{}], falling back to defaults", tmp.c_str()); + } + } + else { + if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { + LogInfo("No rule set configured, using default rules"); + } + else { + LogInfo("Loaded default rule set 'default'"); + } + } + + EQEmu::InitializeDynamicLookups(); + LogInfo("Initialized dynamic dictionary entries"); + } + + + content_service.SetCurrentExpansion(RuleI(Expansion, CurrentExpansion)); + + LogInfo( + "Current expansion is [{}] ({})", + content_service.GetCurrentExpansion(), + Expansion::ExpansionName[content_service.GetCurrentExpansion()] + ); + std::string hotfix_name = ""; bool load_all = true; From f7d4e51da9dc819b40e86fbd5976926531ccb2e1 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:20:50 -0500 Subject: [PATCH 12/20] Filter global_loot [skip ci] --- .../criteria/content_filter_criteria.h | 30 ++++++++--- zone/loottables.cpp | 52 +++++++++++++++---- zone/zone.cpp | 38 +++++++------- 3 files changed, 82 insertions(+), 38 deletions(-) diff --git a/common/repositories/criteria/content_filter_criteria.h b/common/repositories/criteria/content_filter_criteria.h index 9a86dcb99..1fd5f2267 100644 --- a/common/repositories/criteria/content_filter_criteria.h +++ b/common/repositories/criteria/content_filter_criteria.h @@ -26,32 +26,46 @@ #include "../../string_util.h" namespace ContentFilterCriteria { - static std::string apply() + static std::string apply(std::string table_prefix = "") { std::string criteria; + if (!table_prefix.empty()) { + table_prefix = table_prefix + "."; + } + criteria += fmt::format( - " AND (min_expansion <= {} OR min_expansion = 0)", - content_service.GetCurrentExpansion() + " AND ({}min_expansion <= {} OR {}min_expansion = 0)", + table_prefix, + content_service.GetCurrentExpansion(), + table_prefix ); criteria += fmt::format( - " AND (max_expansion >= {} OR max_expansion = 0)", - content_service.GetCurrentExpansion() + " AND ({}max_expansion >= {} OR {}max_expansion = 0)", + table_prefix, + content_service.GetCurrentExpansion(), + table_prefix ); std::vector flags = content_service.GetContentFlags(); - for (auto &flag: flags) { + + for (auto &flag: flags) { flag = "'" + flag + "'"; } std::string flags_in_filter; if (!flags.empty()) { - flags_in_filter = fmt::format(" OR content_flags IN ({})", implode(", ", flags)); + flags_in_filter = fmt::format( + " OR {}content_flags IN ({})", + table_prefix, + implode(", ", flags) + ); } criteria += fmt::format( - " AND (content_flags IS NULL{})", + " AND ({}content_flags IS NULL{})", + table_prefix, flags_in_filter ); diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 0e5ba8d60..666b3d55e 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -27,6 +27,7 @@ #include "npc.h" #include "zonedb.h" #include "global_loot_manager.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include #include @@ -463,42 +464,70 @@ void NPC::CheckGlobalLootTables() void ZoneDatabase::LoadGlobalLoot() { - auto query = StringFormat("SELECT id, loottable_id, description, min_level, max_level, rare, raid, race, " - "class, bodytype, zone, hot_zone FROM global_loot WHERE enabled = 1"); + auto query = fmt::format( + SQL + ( + SELECT + id, + loottable_id, + description, + min_level, + max_level, + rare, + raid, + race, + class, + bodytype, + zone, + hot_zone + FROM + global_loot + WHERE + enabled = 1 + {} + ), + ContentFilterCriteria::apply() + ); auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) + if (!results.Success() || results.RowCount() == 0) { return; + } // we might need this, lets not keep doing it in a loop - auto zoneid = std::to_string(zone->GetZoneID()); - for (auto row = results.begin(); row != results.end(); ++row) { + auto zoneid = std::to_string(zone->GetZoneID()); + for (auto row = results.begin(); row != results.end(); ++row) { // checking zone limits if (row[10]) { auto zones = SplitString(row[10], '|'); auto it = std::find(zones.begin(), zones.end(), zoneid); - if (it == zones.end()) // not in here, skip + if (it == zones.end()) { // not in here, skip continue; + } } GlobalLootEntry e(atoi(row[0]), atoi(row[1]), row[2] ? row[2] : ""); auto min_level = atoi(row[3]); - if (min_level) + if (min_level) { e.AddRule(GlobalLoot::RuleTypes::LevelMin, min_level); + } auto max_level = atoi(row[4]); - if (max_level) + if (max_level) { e.AddRule(GlobalLoot::RuleTypes::LevelMax, max_level); + } // null is not used - if (row[5]) + if (row[5]) { e.AddRule(GlobalLoot::RuleTypes::Rare, atoi(row[5])); + } // null is not used - if (row[6]) + if (row[6]) { e.AddRule(GlobalLoot::RuleTypes::Raid, atoi(row[6])); + } if (row[7]) { auto races = SplitString(row[7], '|'); @@ -522,8 +551,9 @@ void ZoneDatabase::LoadGlobalLoot() } // null is not used - if (row[11]) + if (row[11]) { e.AddRule(GlobalLoot::RuleTypes::HotZone, atoi(row[11])); + } zone->AddGlobalLootEntry(e); } diff --git a/zone/zone.cpp b/zone/zone.cpp index 5358d9a22..3c96e2b91 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -551,32 +551,32 @@ void Zone::GetMerchantDataForZoneLoad() { std::string query = fmt::format( SQL ( SELECT - DISTINCT ml.merchantid, - ml.slot, - ml.item, - ml.faction_required, - ml.level_required, - ml.alt_currency_cost, - ml.classes_required, - ml.probability + DISTINCT merchantlist.merchantid, + merchantlist.slot, + merchantlist.item, + merchantlist.faction_required, + merchantlist.level_required, + merchantlist.alt_currency_cost, + merchantlist.classes_required, + merchantlist.probability FROM - merchantlist AS ml, - npc_types AS nt, - spawnentry AS se, - spawn2 AS s2 + merchantlist, + npc_types, + spawnentry, + spawn2 WHERE - nt.merchant_id = ml.merchantid - AND nt.id = se.npcid - AND se.spawngroupid = s2.spawngroupid - AND s2.zone = '{}' - AND s2.version = {} + npc_types.merchant_id = merchantlist.merchantid + AND npc_types.id = spawnentry.npcid + AND spawnentry.spawngroupid = spawn2.spawngroupid + AND spawn2.zone = '{}' + AND spawn2.version = {} {} ORDER BY - ml.slot + merchantlist.slot ), GetShortName(), GetInstanceVersion(), - ContentFilterCriteria::apply() + ContentFilterCriteria::apply("merchantlist") ); auto results = content_db.QueryDatabase(query); From dadd674b6343be7739ff77be444c34a940e5ba8a Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:30:02 -0500 Subject: [PATCH 13/20] Filter fishing [skip ci] --- zone/forage.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/zone/forage.cpp b/zone/forage.cpp index f14f1f968..56456bd36 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -30,6 +30,7 @@ #include "titles.h" #include "water_map.h" #include "zonedb.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include @@ -109,9 +110,24 @@ uint32 ZoneDatabase::GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, chance[c]=0; } - std::string query = StringFormat("SELECT itemid, chance, npc_id, npc_chance " - "FROM fishing WHERE (zoneid = '%i' || zoneid = 0) AND skill_level <= '%i'", - ZoneID, skill); + std::string query = fmt::format( + SQL( + SELECT + itemid, + chance, + npc_id, + npc_chance + FROM + fishing + WHERE + (zoneid = '{}' || zoneid = 0) + AND skill_level <= '{}' + {} + ), + ZoneID, + skill, + ContentFilterCriteria::apply() + ); auto results = QueryDatabase(query); if (!results.Success()) { return 0; From 68a2af1bcedfca025b0d0d6a118cff91bbcd4531 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:37:00 -0500 Subject: [PATCH 14/20] Filter forage [skip ci] --- zone/forage.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/zone/forage.cpp b/zone/forage.cpp index 56456bd36..6d24f50a8 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -55,9 +55,25 @@ uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) { } uint32 chancepool = 0; - std::string query = StringFormat("SELECT itemid, chance FROM " - "forage WHERE zoneid = '%i' and level <= '%i' " - "LIMIT %i", ZoneID, skill, FORAGE_ITEM_LIMIT); + std::string query = fmt::format( + SQL( + SELECT + itemid, + chance + FROM + forage + WHERE + zoneid = '{}' + and level <= '{}' + {} + LIMIT + {} + ), + ZoneID, + skill, + ContentFilterCriteria::apply(), + FORAGE_ITEM_LIMIT + ); auto results = QueryDatabase(query); if (!results.Success()) { return 0; From 9fbcd99be23c9e901291674e2716b996bfac4571 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:40:40 -0500 Subject: [PATCH 15/20] Filter ground spawns [skip ci] --- zone/object.cpp | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/zone/object.cpp b/zone/object.cpp index 3361b0e44..aa0564c9f 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -26,6 +26,7 @@ #include "quest_parser_collection.h" #include "zonedb.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include @@ -460,7 +461,7 @@ void Object::RandomSpawn(bool send_packet) { m_data.x = zone->random.Real(m_min_x, m_max_x); m_data.y = zone->random.Real(m_min_y, m_max_y); - + if(m_data.z == BEST_Z_INVALID) { glm::vec3 me; me.x = m_data.x; @@ -470,11 +471,11 @@ void Object::RandomSpawn(bool send_packet) { float best_z = zone->zonemap->FindClosestZ(me, &hit); if (best_z != BEST_Z_INVALID) { m_data.z = best_z + 0.1f; - } + } } LogInfo("Object::RandomSpawn([{}]): [{}] ([{}], [{}], [{}])", m_data.object_name, m_inst->GetID(), m_data.x, m_data.y, m_data.z); - + respawn_timer.Disable(); if(send_packet) { @@ -520,7 +521,7 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) co->drop_id = 0; entity_list.QueueClients(nullptr, outapp, false); safe_delete(outapp); - + // No longer using a tradeskill object sender->SetTradeskillObject(nullptr); user = nullptr; @@ -681,7 +682,7 @@ void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Objec "size = %f, tilt_x = %f, tilt_y = %f " "WHERE id = %i", object.zone_id, object.x, object.y, object.z, object.heading, - item_id, charges, object_name, type, icon, + item_id, charges, object_name, type, icon, object.size, object.tilt_x, object.tilt_y, id); safe_delete_array(object_name); auto results = QueryDatabase(query); @@ -698,14 +699,20 @@ void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Objec // Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs) { - std::string query = StringFormat("SELECT max_x, max_y, max_z, " - "min_x, min_y, heading, name, " - "item, max_allowed, respawn_timer " - "FROM ground_spawns " - "WHERE zoneid = %i AND (version = %u OR version = -1) " - "LIMIT 50", zone_id, version); - auto results = QueryDatabase(query); - if (!results.Success()) { + std::string query = StringFormat( + "SELECT max_x, max_y, max_z, " + "min_x, min_y, heading, name, " + "item, max_allowed, respawn_timer " + "FROM ground_spawns " + "WHERE zoneid = %i AND (version = %u OR version = -1) %s " + "LIMIT 50", + zone_id, + version, + ContentFilterCriteria::apply().c_str() + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { return gs; } From 91c874a3105c1b8b0421666df4c4b5f3cd38c644 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:43:09 -0500 Subject: [PATCH 16/20] Filter starting_items [skip ci] --- common/shareddb.cpp | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 358a80d00..86f497d89 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -432,16 +432,25 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, EQEmu::InventoryProfile* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { - const EQEmu::ItemData* myitem; + const EQEmu::ItemData *myitem; - std::string query = StringFormat("SELECT itemid, item_charges, slot FROM starting_items " - "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " - "(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND " - "gm <= %i ORDER BY id", - si_race, si_class, si_deity, si_current_zone, admin_level); - auto results = QueryDatabase(query); - if (!results.Success()) - return false; + std::string query = StringFormat( + "SELECT itemid, item_charges, slot FROM starting_items " + "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " + "(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND " + "gm <= %i %s ORDER BY id", + si_race, + si_class, + si_deity, + si_current_zone, + admin_level, + ContentFilterCriteria::apply().c_str() + ); + + auto results = QueryDatabase(query); + if (!results.Success()) { + return false; + } for (auto row = results.begin(); row != results.end(); ++row) { @@ -2129,7 +2138,7 @@ void SharedDatabase::LoadLootDrops(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; LootDrop_Struct *ld = reinterpret_cast(loot_drop); - + const std::string query = fmt::format( SQL( SELECT From 9aac84a4050b650539f818e1461c4d6bf7046310 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:45:45 -0500 Subject: [PATCH 17/20] filter traps [skip ci] --- zone/trap.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/zone/trap.cpp b/zone/trap.cpp index 7d205e563..b490f5c8d 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -24,6 +24,7 @@ #include "entity.h" #include "mob.h" #include "trap.h" +#include "../common/repositories/criteria/content_filter_criteria.h" /* @@ -104,7 +105,7 @@ bool Trap::Process() reset_timer.Disable(); charid = 0; } - + if (respawn_timer.Enabled() && respawn_timer.Check()) { detected = false; @@ -225,13 +226,13 @@ void Trap::Trigger(Mob* trigger) safe_delete(outapp); } } - + if (trigger && trigger->IsClient()) { trigger->CastToClient()->trapid = trap_id; charid = trigger->CastToClient()->CharacterID(); } - + bool update = false; if (despawn_when_triggered) { @@ -242,16 +243,16 @@ void Trap::Trigger(Mob* trigger) { reset_timer.Start(5000); } - + if (triggered_number > 0) ++times_triggered; - + if (triggered_number > 0 && triggered_number <= times_triggered) { Log(Logs::General, Logs::Traps, "Triggered number for trap %d reached. %d/%d", trap_id, times_triggered, triggered_number); update = true; } - + if (update) { UpdateTrap(); @@ -290,7 +291,7 @@ Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist, float &trap_curd } else trap_curdist = INVALID_INDEX; - + return current_trap; } @@ -399,9 +400,14 @@ void EntityList::ClearTrapPointers() bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { - std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " + std::string query = StringFormat( + "SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, " - "`group`, triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND version=%u", zonename, version); + "`group`, triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND version=%u %s", + zonename, + version, + ContentFilterCriteria::apply().c_str() + ); auto results = QueryDatabase(query); if (!results.Success()) { From 2f4a217f7ae87a2a2fa4a3513e96f091fbff158f Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:49:04 -0500 Subject: [PATCH 18/20] Filter zone_points [skip ci] --- zone/zone.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 3c96e2b91..8498b865b 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1841,16 +1841,22 @@ ZonePoint* Zone::GetClosestZonePointWithoutZone(float x, float y, float z, Clien return closest_zp; } -bool ZoneDatabase::LoadStaticZonePoints(LinkedList* zone_point_list, const char* zonename, uint32 version) +bool ZoneDatabase::LoadStaticZonePoints(LinkedList *zone_point_list, const char *zonename, uint32 version) { zone_point_list->Clear(); zone->numzonepoints = 0; - std::string query = StringFormat("SELECT x, y, z, target_x, target_y, " - "target_z, target_zone_id, heading, target_heading, " - "number, target_instance, client_version_mask " - "FROM zone_points WHERE zone='%s' AND (version=%i OR version=-1) " - "ORDER BY number", - zonename, version); + + std::string query = StringFormat( + "SELECT x, y, z, target_x, target_y, " + "target_z, target_zone_id, heading, target_heading, " + "number, target_instance, client_version_mask " + "FROM zone_points WHERE zone='%s' AND (version=%i OR version=-1) %s" + "ORDER BY number", + zonename, + version, + ContentFilterCriteria::apply().c_str() + ); + auto results = QueryDatabase(query); if (!results.Success()) { return false; From c8722ea789ccd297cc22d4c924869956c5ab3325 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 19:58:04 -0500 Subject: [PATCH 19/20] Filter start_zones [skip ci] --- world/worlddb.cpp | 19 +++++++++++-------- zone/client_packet.cpp | 11 ++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/world/worlddb.cpp b/world/worlddb.cpp index b7bec5459..cf6411cc5 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -25,6 +25,7 @@ #include #include #include "sof_char_create_data.h" +#include "../common/repositories/criteria/content_filter_criteria.h" WorldDatabase database; WorldDatabase content_db; @@ -197,10 +198,11 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o if (has_home == 0 || has_bind == 0) { character_list_query = StringFormat( - "SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", + "SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i %s", p_character_select_entry_struct->Class, p_character_select_entry_struct->Deity, - p_character_select_entry_struct->Race + p_character_select_entry_struct->Race, + ContentFilterCriteria::apply().c_str() ); auto results_bind = content_db.QueryDatabase(character_list_query); for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { @@ -313,7 +315,7 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o if (matslot > 6) { uint32 item_id_file = 0; - // Weapon Models + // Weapon Models if (inst->GetOrnamentationIDFile() != 0) { item_id_file = inst->GetOrnamentationIDFile(); p_character_select_entry_struct->Equip[matslot].Material = item_id_file; @@ -381,7 +383,7 @@ int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) heading = atof(row[5]); } - query = StringFormat("UPDATE character_data SET zone_id = '%d', zone_instance = '%d', x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = %u", + query = StringFormat("UPDATE character_data SET zone_id = '%d', zone_instance = '%d', x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = %u", zone_id, instance_id, x, y, z, heading, CharID); results = database.QueryDatabase(query); @@ -423,11 +425,12 @@ bool WorldDatabase::GetStartZone( // see if we have an entry for start_zone. We can support both titanium & SOF+ by having two entries per class/race/deity combo with different zone_ids std::string query = StringFormat( "SELECT x, y, z, heading, start_zone, bind_id, bind_x, bind_y, bind_z FROM start_zones WHERE zone_id = %i " - "AND player_class = %i AND player_deity = %i AND player_race = %i", + "AND player_class = %i AND player_deity = %i AND player_race = %i %s", p_char_create_struct->start_zone, p_char_create_struct->class_, p_char_create_struct->deity, - p_char_create_struct->race + p_char_create_struct->race, + ContentFilterCriteria::apply().c_str() ); auto results = QueryDatabase(query); @@ -469,7 +472,7 @@ bool WorldDatabase::GetStartZone( void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){ if (in_cc->start_zone == RuleI(World, TutorialZoneID)) { in_pp->zone_id = in_cc->start_zone; - } + } else { in_pp->x = in_pp->binds[0].x = -51; in_pp->y = in_pp->binds[0].y = -20; @@ -805,4 +808,4 @@ bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQEmu::In } return true; -} \ No newline at end of file +} diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f4a53ad8b..8005e5dec 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12437,9 +12437,14 @@ void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) uint32 zoneid = 0; uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); + std::string query = StringFormat( + "SELECT zone_id, bind_id, x, y, z FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i %s", + m_pp.class_, + m_pp.deity, + m_pp.race, + ContentFilterCriteria::apply().c_str() + ); auto results = content_db.QueryDatabase(query); if (!results.Success()) { LogError("No valid start zones found for /setstartcity"); From 0cce61e85ddd74e9e80174b5104595d67fbf1d2d Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 5 Apr 2020 20:11:05 -0500 Subject: [PATCH 20/20] Filter GetZoneCFG [skip ci] --- zone/zonedb.cpp | 60 ++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b68fe2284..d290ff7fc 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -12,6 +12,7 @@ #include "zone.h" #include "zonedb.h" #include "aura.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include #include @@ -83,19 +84,19 @@ bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct } bool ZoneDatabase::GetZoneCFG( - uint32 zoneid, - uint16 instance_id, - NewZone_Struct *zone_data, - bool &can_bind, - bool &can_combat, - bool &can_levitate, - bool &can_castoutdoor, - bool &is_city, - bool &is_hotzone, - bool &allow_mercs, + uint32 zoneid, + uint16 instance_id, + NewZone_Struct *zone_data, + bool &can_bind, + bool &can_combat, + bool &can_levitate, + bool &can_castoutdoor, + bool &is_city, + bool &is_hotzone, + bool &allow_mercs, double &max_movement_update_range, - uint8 &zone_type, - int &ruleset, + uint8 &zone_type, + int &ruleset, char **map_filename) { *map_filename = new char[100]; @@ -165,8 +166,11 @@ bool ZoneDatabase::GetZoneCFG( "fast_regen_endurance, " // 59 "npc_max_aggro_dist, " // 60 "max_movement_update_range " // 61 - "FROM zone WHERE zoneidnumber = %i AND version = %i", - zoneid, instance_id); + "FROM zone WHERE zoneidnumber = %i AND version = %i %s", + zoneid, + instance_id, + ContentFilterCriteria::apply().c_str() + ); auto results = QueryDatabase(query); if (!results.Success()) { strcpy(*map_filename, "default"); @@ -361,7 +365,7 @@ void ZoneDatabase::RegisterBug(BugReport_Struct* bug_report) { char* type_ = nullptr; char* target_ = nullptr; char* bug_ = nullptr; - + len = strlen(bug_report->reporter_name); if (len) { if (len > 63) // check against db column size @@ -427,7 +431,7 @@ void ZoneDatabase::RegisterBug(BugReport_Struct* bug_report) { safe_delete_array(type_); safe_delete_array(target_); safe_delete_array(bug_); - + QueryDatabase(query); } @@ -585,7 +589,7 @@ void ZoneDatabase::RegisterBug(Client* client, BugReport_Struct* bug_report) { safe_delete_array(target_name_); safe_delete_array(bug_report_); safe_delete_array(system_info_); - + auto result = QueryDatabase(query); // TODO: Entity dumping [RuleB(Bugs, DumpTargetEntity)] @@ -1244,9 +1248,9 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str "`character_spells` " "WHERE `id` = %u ORDER BY `slot_id`", character_id); auto results = database.QueryDatabase(query); - + /* Initialize Spells */ - + memset(pp->spell_book, 0xFF, (sizeof(uint32) * EQEmu::spells::SPELLBOOK_SIZE)); // We have the ability to block loaded spells by max id on a per-client basis.. @@ -1262,7 +1266,7 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str continue; if (id < 3 || id > SPDAT_RECORDS) // 3 ("Summon Corpse") is the first scribable spell in spells_us.txt continue; - + pp->spell_book[idx] = id; } @@ -1606,11 +1610,11 @@ bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_ } bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ - + /* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */ if (account_id <= 0) return false; - + std::string mail_key = database.GetMailKey(character_id); clock_t t = std::clock(); /* Function timer start */ @@ -4090,9 +4094,9 @@ bool ZoneDatabase::LoadFactionData() faction_array = new Faction *[max_faction + 1]; memset(faction_array, 0, (sizeof(Faction*) * (max_faction + 1))); - + std::vector faction_ids; - + // load factions query = "SELECT `id`, `name`, `base` FROM `faction_list`"; @@ -4120,7 +4124,7 @@ bool ZoneDatabase::LoadFactionData() faction_array[index]->base = atoi(fr_row[2]); faction_array[index]->min = MIN_PERSONAL_FACTION; faction_array[index]->max = MAX_PERSONAL_FACTION; - + faction_ids.push_back(index); } @@ -4156,7 +4160,7 @@ bool ZoneDatabase::LoadFactionData() else { LogInfo("Unable to load Faction Base data..."); } - + // load race, class and diety modifiers query = fmt::format("SELECT `faction_id`, `mod`, `mod_name` FROM `faction_list_mod` WHERE `faction_id` IN ({})", faction_id_criteria); @@ -4879,7 +4883,7 @@ uint32 ZoneDatabase::LoadSaylinkID(const char* saylink_text, bool auto_insert) { if (!saylink_text || saylink_text[0] == '\0') return 0; - + std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1", saylink_text); auto results = QueryDatabase(query); if (!results.Success()) @@ -4904,6 +4908,6 @@ uint32 ZoneDatabase::SaveSaylinkID(const char* saylink_text) auto results = QueryDatabase(query); if (!results.Success()) return 0; - + return results.LastInsertedID(); }