Skip to content

Commit d182fc3

Browse files
authored
[Expansions] Zone Expansion Version Routing (#4084)
* Expansion version routing * CheckForImproperContentFiles rule * Update world_content_service.cpp * Update client.cpp * Update client.cpp * Update CheckForImproperContentFiles * Remove nek pok check * Remove file checking * Remove * Command and dev tools menu tweaks * Update world_content_service.cpp * Update world_content_service.cpp * Update version path * Update content_filter_criteria.h * Update content_filter_criteria.h * Update quest_parser_collection.cpp * Update comments * PR feedback * Update client_packet.cpp * Remove notes column for display cleanliness
1 parent 5a89fcf commit d182fc3

15 files changed

+263
-56
lines changed

common/content/world_content_service.cpp

Lines changed: 102 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,11 @@
1-
/**
2-
* EQEmulator: Everquest Server Emulator
3-
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
4-
*
5-
* This program is free software; you can redistribute it and/or modify
6-
* it under the terms of the GNU General Public License as published by
7-
* the Free Software Foundation; version 2 of the License.
8-
*
9-
* This program is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY except by those people which sell it, which
11-
* are required to give you total support for your newly bought product;
12-
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13-
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14-
*
15-
* You should have received a copy of the GNU General Public License
16-
* along with this program; if not, write to the Free Software
17-
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18-
*
19-
*/
20-
211
#include "world_content_service.h"
2+
3+
#include <utility>
4+
#include <glm/vec3.hpp>
225
#include "../database.h"
236
#include "../rulesys.h"
247
#include "../eqemu_logsys.h"
25-
#include "../repositories/content_flags_repository.h"
8+
#include "../repositories/instance_list_repository.h"
269

2710

2811
WorldContentService::WorldContentService()
@@ -119,7 +102,7 @@ std::vector<std::string> WorldContentService::GetContentFlagsDisabled()
119102
/**
120103
* @param content_flags
121104
*/
122-
void WorldContentService::SetContentFlags(const std::vector<ContentFlagsRepository::ContentFlags>& content_flags)
105+
void WorldContentService::SetContentFlags(const std::vector<ContentFlagsRepository::ContentFlags> &content_flags)
123106
{
124107
WorldContentService::content_flags = content_flags;
125108
}
@@ -167,14 +150,14 @@ bool WorldContentService::DoesPassContentFiltering(const ContentFlags &f)
167150
}
168151

169152
// if we don't have any enabled flag in enabled flags, we fail
170-
for (const auto& flag: Strings::Split(f.content_flags)) {
153+
for (const auto &flag: Strings::Split(f.content_flags)) {
171154
if (!Strings::Contains(GetContentFlagsEnabled(), flag)) {
172155
return false;
173156
}
174157
}
175158

176159
// if we don't have any disabled flag in disabled flags, we fail
177-
for (const auto& flag: Strings::Split(f.content_flags_disabled)) {
160+
for (const auto &flag: Strings::Split(f.content_flags_disabled)) {
178161
if (!Strings::Contains(GetContentFlagsDisabled(), flag)) {
179162
return false;
180163
}
@@ -200,6 +183,7 @@ void WorldContentService::ReloadContentFlags()
200183
}
201184

202185
SetContentFlags(set_content_flags);
186+
SetContentZones(ZoneRepository::All(*m_content_database));
203187
}
204188

205189
Database *WorldContentService::GetDatabase() const
@@ -214,6 +198,18 @@ WorldContentService *WorldContentService::SetDatabase(Database *database)
214198
return this;
215199
}
216200

201+
Database *WorldContentService::GetContentDatabase() const
202+
{
203+
return m_content_database;
204+
}
205+
206+
WorldContentService *WorldContentService::SetContentDatabase(Database *database)
207+
{
208+
WorldContentService::m_content_database = database;
209+
210+
return this;
211+
}
212+
217213
void WorldContentService::SetContentFlag(const std::string &content_flag_name, bool enabled)
218214
{
219215
auto flags = ContentFlagsRepository::GetWhere(
@@ -238,3 +234,85 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
238234

239235
ReloadContentFlags();
240236
}
237+
238+
// SetZones sets the zones for the world content service
239+
// this is used for zone routing middleware
240+
// we pull the zone list from the zone repository and feed from the zone store for now
241+
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
242+
WorldContentService *WorldContentService::SetContentZones(const std::vector<BaseZoneRepository::Zone>& zones)
243+
{
244+
m_zones = zones;
245+
246+
LogInfo("Loaded [{}] zones", m_zones.size());
247+
248+
return this;
249+
}
250+
251+
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
252+
//
253+
// example # 1
254+
// lavastorm (pre-don) version 0 (classic)
255+
// lavastorm (don) version 1
256+
// we want to route players to the correct version of lavastorm based on the current server side expansion
257+
// in order to do that the simplest and cleanest way we intercept the zoning process and route players to an "instance" of the zone
258+
// the reason why we're doing this is because all of the zoning logic already is handled by two keys "zone_id" and "instance_id"
259+
// we can leverage static, never expires instances to handle this but to the client they don't see it any other way than a public normal zone
260+
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
261+
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
262+
// we decide to route the client to the correct version of the zone based on the current server side expansion
263+
// example # 2
264+
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
265+
{
266+
// if we're already in an instance, we don't want to route the player to another instance
267+
if (zc->instanceID > 0) {
268+
return;
269+
}
270+
271+
for (auto &z: m_zones) {
272+
if (z.zoneidnumber == zc->zoneID) {
273+
auto f = ContentFlags{
274+
.min_expansion = z.min_expansion,
275+
.max_expansion = z.max_expansion,
276+
.content_flags = z.content_flags,
277+
.content_flags_disabled = z.content_flags_disabled
278+
};
279+
280+
if (DoesPassContentFiltering(f)) {
281+
LogInfo(
282+
"Attempting to route player to zone [{}] ({}) version [{}] long_name [{}]",
283+
z.short_name,
284+
z.zoneidnumber,
285+
z.version,
286+
z.long_name
287+
);
288+
289+
auto instances = InstanceListRepository::GetWhere(
290+
*GetDatabase(),
291+
fmt::format(
292+
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
293+
z.zoneidnumber,
294+
z.version
295+
)
296+
);
297+
298+
if (!instances.empty()) {
299+
auto instance = instances.front();
300+
zc->instanceID = instance.id;
301+
302+
LogInfo(
303+
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
304+
instance.id,
305+
z.short_name,
306+
z.zoneidnumber,
307+
z.version,
308+
z.long_name,
309+
instance.notes
310+
);
311+
312+
break;
313+
}
314+
}
315+
}
316+
}
317+
}
318+

common/content/world_content_service.h

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
1-
/**
2-
* EQEmulator: Everquest Server Emulator
3-
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
4-
*
5-
* This program is free software; you can redistribute it and/or modify
6-
* it under the terms of the GNU General Public License as published by
7-
* the Free Software Foundation; version 2 of the License.
8-
*
9-
* This program is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY except by those people which sell it, which
11-
* are required to give you total support for your newly bought product;
12-
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13-
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14-
*
15-
* You should have received a copy of the GNU General Public License
16-
* along with this program; if not, write to the Free Software
17-
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18-
*
19-
*/
20-
211
#ifndef EQEMU_WORLD_CONTENT_SERVICE_H
222
#define EQEMU_WORLD_CONTENT_SERVICE_H
233

244
#include <string>
255
#include <vector>
266
#include "../repositories/content_flags_repository.h"
7+
#include "../repositories/zone_repository.h"
278

289
class Database;
2910

@@ -182,14 +163,23 @@ class WorldContentService {
182163
WorldContentService * SetDatabase(Database *database);
183164
Database *GetDatabase() const;
184165

166+
WorldContentService * SetContentDatabase(Database *database);
167+
Database *GetContentDatabase() const;
168+
185169
void SetContentFlag(const std::string &content_flag_name, bool enabled);
186170

171+
void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc);
172+
WorldContentService * SetContentZones(const std::vector<ZoneRepository::Zone>& zones);
187173
private:
188174
int current_expansion{};
189175
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
190176

191177
// reference to database
192178
Database *m_database;
179+
Database *m_content_database;
180+
181+
// holds a record of the zone table from the database
182+
std::vector<ZoneRepository::Zone> m_zones = {};
193183
};
194184

195185
extern WorldContentService content_service;

common/database/database_update_manifest.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5365,6 +5365,33 @@ ALTER TABLE `character_corpses` MODIFY COLUMN `time_of_death` datetime NOT NULL
53655365
.sql = R"(
53665366
ALTER TABLE `object_contents` MODIFY COLUMN `droptime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP;
53675367
)"
5368+
},
5369+
ManifestEntry{
5370+
.version = 9263,
5371+
.description = "2024_02_16_rearrange_zone_columns.sql",
5372+
.check = "show columns from zone like 'note'",
5373+
.condition = "missing",
5374+
.match = "varchar(200)",
5375+
.sql = R"(
5376+
ALTER TABLE `zone`
5377+
MODIFY COLUMN `id` int(10) NOT NULL AUTO_INCREMENT FIRST,
5378+
MODIFY COLUMN `zoneidnumber` int(4) NOT NULL DEFAULT 0 AFTER `id`,
5379+
MODIFY COLUMN `version` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `zoneidnumber`,
5380+
MODIFY COLUMN `short_name` varchar(32) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `version`,
5381+
MODIFY COLUMN `long_name` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL AFTER `short_name`,
5382+
MODIFY COLUMN `min_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `long_name`,
5383+
MODIFY COLUMN `note` varchar(200) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `map_file_name`,
5384+
MODIFY COLUMN `min_expansion` tinyint(4) NOT NULL DEFAULT -1 AFTER `note`,
5385+
MODIFY COLUMN `max_expansion` tinyint(4) NOT NULL DEFAULT -1 AFTER `min_expansion`,
5386+
MODIFY COLUMN `content_flags` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `max_expansion`,
5387+
MODIFY COLUMN `content_flags_disabled` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `content_flags`,
5388+
MODIFY COLUMN `expansion` tinyint(3) NOT NULL DEFAULT 0 AFTER `content_flags_disabled`,
5389+
MODIFY COLUMN `file_name` varchar(16) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `expansion`,
5390+
MODIFY COLUMN `safe_x` float NOT NULL DEFAULT 0 AFTER `file_name`,
5391+
MODIFY COLUMN `safe_y` float NOT NULL DEFAULT 0 AFTER `safe_x`,
5392+
MODIFY COLUMN `safe_z` float NOT NULL DEFAULT 0 AFTER `safe_y`,
5393+
MODIFY COLUMN `safe_heading` float NOT NULL DEFAULT 0 AFTER `safe_z`;
5394+
)"
53685395
}
53695396
// -- template; copy/paste this when you need to create a new entry
53705397
// ManifestEntry{

common/ruletypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ RULE_INT(Expansion, AutoGrantAAExpansion, -1, "Expansion to auto grant AAs up to
890890
RULE_CATEGORY_END()
891891

892892
RULE_CATEGORY(Instances)
893-
RULE_INT(Instances, ReservedInstances, 30, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
893+
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
894894
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
895895
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
896896
RULE_CATEGORY_END()

common/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
4343
*/
4444

45-
#define CURRENT_BINARY_DATABASE_VERSION 9262
45+
#define CURRENT_BINARY_DATABASE_VERSION 9263
4646
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9042
4747

4848
#endif

shared_memory/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ int main(int argc, char **argv)
168168

169169
content_service.SetCurrentExpansion(RuleI(Expansion, CurrentExpansion));
170170
content_service.SetDatabase(&database)
171+
->SetContentDatabase(&content_db)
171172
->SetExpansionContext()
172173
->ReloadContentFlags();
173174

world/world_boot.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
381381

382382
LogInfo("Initializing [WorldContentService]");
383383
content_service.SetDatabase(&database)
384+
->SetContentDatabase(&content_db)
384385
->SetExpansionContext()
385386
->ReloadContentFlags();
386387

zone/client.cpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ extern volatile bool RunLoops;
7171
#include "../common/events/player_events.h"
7272
#include "../common/events/player_event_logs.h"
7373
#include "dialogue_window.h"
74+
#include "../common/zone_store.h"
7475

7576

7677
extern QueryServ* QServ;
@@ -9108,6 +9109,7 @@ void Client::ShowDevToolsMenu()
91089109
*/
91099110
menu_show += Saylink::Silent("#showzonepoints", "Zone Points");
91109111
menu_show += " | " + Saylink::Silent("#showzonegloballoot", "Zone Global Loot");
9112+
menu_show += " | " + Saylink::Silent("#show content_flags", "Content Flags");
91119113

91129114
/**
91139115
* Reload
@@ -9165,14 +9167,6 @@ void Client::ShowDevToolsMenu()
91659167

91669168
Message(Chat::White, "Developer Tools Menu");
91679169

9168-
Message(
9169-
Chat::White,
9170-
fmt::format(
9171-
"Current Expansion | {}",
9172-
content_service.GetCurrentExpansionName()
9173-
).c_str()
9174-
);
9175-
91769170
Message(
91779171
Chat::White,
91789172
fmt::format(
@@ -9288,6 +9282,36 @@ void Client::ShowDevToolsMenu()
92889282
);
92899283

92909284
SendChatLineBreak();
9285+
9286+
Message(
9287+
Chat::White,
9288+
fmt::format(
9289+
"Current Expansion | {} ({})",
9290+
content_service.GetCurrentExpansionName(),
9291+
content_service.GetCurrentExpansion()
9292+
).c_str()
9293+
);
9294+
9295+
9296+
auto z = GetZoneVersionWithFallback(zone->GetZoneID(), zone->GetInstanceVersion());
9297+
9298+
if (z) {
9299+
Message(
9300+
Chat::White,
9301+
fmt::format(
9302+
"Current Zone | [{}] ({}) version [{}] instance_id [{}] min/max expansion ({}/{}) content_flags [{}]",
9303+
z->short_name,
9304+
z->long_name,
9305+
z->version,
9306+
zone->GetInstanceID(),
9307+
z->min_expansion,
9308+
z->max_expansion,
9309+
z->content_flags
9310+
).c_str()
9311+
);
9312+
}
9313+
9314+
SendChatLineBreak();
92919315
}
92929316

92939317
void Client::SendChatLineBreak(uint16 color) {

zone/client_packet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,6 @@ void Client::CompleteConnect()
954954
heroforge_wearchange_timer.Start(250);
955955

956956
RecordStats();
957-
958957
AutoGrantAAPoints();
959958

960959
// enforce some rules..
@@ -17138,3 +17137,4 @@ void Client::Handle_OP_GuildTributeDonatePlat(const EQApplicationPacket *app)
1713817137
RequestGuildFavorAndTimer(GuildID());
1713917138
}
1714017139
}
17140+

zone/client_process.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,7 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
10721072
SetMana(GetMaxMana() / 20);
10731073
SetEndurance(GetMaxEndurance() / 20);
10741074
}
1075-
1075+
10761076
if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0) {
10771077
SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base_value[0])))),
10781078
GetAAXP(),true);

0 commit comments

Comments
 (0)