mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
[Zone] Zone Routing Improvements (#4142)
* Routing changes * Update world_content_service.cpp * Cleanup routing logic * Tweaks
This commit is contained in:
parent
70ee95efc0
commit
b6c3e549da
@ -178,12 +178,13 @@ void WorldContentService::ReloadContentFlags()
|
||||
LogInfo(
|
||||
"Loaded content flag [{}] [{}]",
|
||||
f.flag_name,
|
||||
(f.enabled ? "Enabled" : "Disabled")
|
||||
(f.enabled ? "enabled" : "disabled")
|
||||
);
|
||||
}
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
SetContentZones(ZoneRepository::All(*m_content_database));
|
||||
LoadZones();
|
||||
LoadStaticGlobalZoneInstances();
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@ -235,19 +236,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
||||
ReloadContentFlags();
|
||||
}
|
||||
|
||||
// SetZones sets the zones for the world content service
|
||||
// this is used for zone routing middleware
|
||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
||||
WorldContentService *WorldContentService::SetContentZones(const std::vector<BaseZoneRepository::Zone>& zones)
|
||||
{
|
||||
m_zones = zones;
|
||||
|
||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
|
||||
//
|
||||
// example # 1
|
||||
@ -260,16 +248,52 @@ WorldContentService *WorldContentService::SetContentZones(const std::vector<Base
|
||||
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
|
||||
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
||||
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
||||
// example # 2
|
||||
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
{
|
||||
// if we're already in an instance, we don't want to route the player to another instance
|
||||
if (zc->instanceID > 0) {
|
||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||
if (r.zone_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
zc->instanceID = r.instance.id;
|
||||
}
|
||||
|
||||
// LoadStaticGlobalZoneInstances loads all static global zone instances
|
||||
// these are zones that are never set to expire and are global
|
||||
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
|
||||
WorldContentService * WorldContentService::LoadStaticGlobalZoneInstances()
|
||||
{
|
||||
m_zone_instances = InstanceListRepository::GetWhere(*GetDatabase(), fmt::format("never_expires = 1 AND is_global = 1"));
|
||||
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// LoadZones sets the zones for the world content service
|
||||
// this is used for zone routing middleware
|
||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
||||
WorldContentService * WorldContentService::LoadZones()
|
||||
{
|
||||
m_zones = ZoneRepository::All(*GetContentDatabase());
|
||||
|
||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// FindZone is critical to the zone routing middleware and any logic that needs to route players to the correct zone
|
||||
// era contextual routing, multiple version of zones, etc
|
||||
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
// if we're already in a regular instance, we don't want to route the player to another instance
|
||||
if (instance_id > RuleI(Instances, ReservedInstances)) {
|
||||
return WorldContentService::FindZoneResult{};
|
||||
}
|
||||
|
||||
for (auto &z: m_zones) {
|
||||
if (z.zoneidnumber == zc->zoneID) {
|
||||
if (z.zoneidnumber == zone_id) {
|
||||
auto f = ContentFlags{
|
||||
.min_expansion = z.min_expansion,
|
||||
.max_expansion = z.max_expansion,
|
||||
@ -286,33 +310,45 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
z.long_name
|
||||
);
|
||||
|
||||
auto instances = InstanceListRepository::GetWhere(
|
||||
*GetDatabase(),
|
||||
fmt::format(
|
||||
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
|
||||
z.zoneidnumber,
|
||||
z.version
|
||||
)
|
||||
// first pass, explicit match on public static global zone instances
|
||||
for (auto &i: m_zone_instances) {
|
||||
if (i.zone == zone_id && i.version == z.version) {
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
z.note
|
||||
);
|
||||
|
||||
if (!instances.empty()) {
|
||||
auto instance = instances.front();
|
||||
zc->instanceID = instance.id;
|
||||
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
instance.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
instance.notes
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = InstanceListRepository::NewEntity(),
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WorldContentService::FindZoneResult{};
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
#include "../repositories/content_flags_repository.h"
|
||||
#include "../repositories/zone_repository.h"
|
||||
#include "../repositories/instance_list_repository.h"
|
||||
|
||||
class Database;
|
||||
|
||||
@ -169,7 +170,14 @@ public:
|
||||
void SetContentFlag(const std::string &content_flag_name, bool enabled);
|
||||
|
||||
void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc);
|
||||
WorldContentService * SetContentZones(const std::vector<ZoneRepository::Zone>& zones);
|
||||
|
||||
struct FindZoneResult {
|
||||
uint32 zone_id = 0;
|
||||
InstanceListRepository::InstanceList instance;
|
||||
ZoneRepository::Zone zone;
|
||||
};
|
||||
|
||||
FindZoneResult FindZone(uint32 zone_id, uint32 instance_id);
|
||||
private:
|
||||
int current_expansion{};
|
||||
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
|
||||
@ -180,6 +188,9 @@ private:
|
||||
|
||||
// holds a record of the zone table from the database
|
||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
||||
WorldContentService * LoadZones();
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
#include "../common/repositories/player_event_logs_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@ -771,6 +772,18 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto r = content_service.FindZone(zone_id, instance_id);
|
||||
if (r.zone_id && r.instance.id != instance_id) {
|
||||
LogInfo(
|
||||
"Zone [{}] has been remapped to instance_id [{}] from instance_id [{}] for client [{}]",
|
||||
r.zone.short_name,
|
||||
r.instance.id,
|
||||
instance_id,
|
||||
char_name
|
||||
);
|
||||
instance_id = r.instance.id;
|
||||
}
|
||||
|
||||
// Make sure this account owns this character
|
||||
if (temporary_account_id != account_id) {
|
||||
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
|
||||
|
||||
@ -142,6 +142,7 @@ std::vector<Reload> reload_types = {
|
||||
Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"},
|
||||
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
|
||||
Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"},
|
||||
Reload{.command = "content_flags", .opcode = ServerOP_ReloadContentFlags, .desc = "Content Flags"},
|
||||
Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"},
|
||||
Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
|
||||
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
|
||||
|
||||
@ -188,6 +188,11 @@ int main(int argc, char **argv)
|
||||
RegisterConsoleFunctions(console);
|
||||
}
|
||||
|
||||
content_service.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->SetExpansionContext()
|
||||
->ReloadContentFlags();
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
|
||||
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
|
||||
|
||||
|
||||
@ -6285,7 +6285,17 @@ void Client::SendZonePoints()
|
||||
zp->zpe[i].z = data->target_z;
|
||||
zp->zpe[i].heading = data->target_heading;
|
||||
zp->zpe[i].zoneid = data->target_zone_id;
|
||||
zp->zpe[i].zoneinstance = data->target_zone_instance;
|
||||
|
||||
// if the target zone is the same as the current zone, use the instance of the current zone
|
||||
// if we don't use the same instance_id that the client was sent, the client will forcefully
|
||||
// issue a zone change request when they should be simply moving to a different point in the same zone
|
||||
// because the client will think the zone point target is different from the current instance
|
||||
auto target_instance = data->target_zone_instance;
|
||||
if (data->target_zone_id == zone->GetZoneID() && data->target_zone_instance == 0) {
|
||||
target_instance = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
zp->zpe[i].zoneinstance = target_instance;
|
||||
i++;
|
||||
}
|
||||
iterator.Advance();
|
||||
|
||||
@ -73,6 +73,15 @@ Doors::Doors(const DoorsRepository::Doors &door) :
|
||||
m_door_param = door.door_param;
|
||||
m_size = door.size;
|
||||
m_invert_state = door.invert_state;
|
||||
|
||||
// if the target zone is the same as the current zone, use the instance of the current zone
|
||||
// if we don't use the same instance_id that the client was sent, the client will forcefully
|
||||
// issue a zone change request when they should be simply moving to a different point in the same zone
|
||||
// because the client will think the zone point target is different from the current instance
|
||||
if (door.dest_zone == zone->GetShortName() && m_destination_instance_id == 0) {
|
||||
m_destination_instance_id = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
m_destination_instance_id = door.dest_instance;
|
||||
m_is_ldon_door = door.is_ldon_door;
|
||||
m_dz_switch_id = door.dz_switch_id;
|
||||
|
||||
@ -403,7 +403,6 @@ int main(int argc, char **argv)
|
||||
|
||||
content_service.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->SetContentZones(zone_store.GetZones())
|
||||
->SetExpansionContext()
|
||||
->ReloadContentFlags();
|
||||
|
||||
|
||||
@ -405,11 +405,23 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
zc->success
|
||||
);
|
||||
|
||||
strcpy(zc2->char_name, zc->char_name);
|
||||
strn0cpy(zc2->char_name, zc->char_name, 64);
|
||||
zc2->zoneID = zone->GetZoneID();
|
||||
zc2->success = 1;
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
zc2->instanceID = zone->GetInstanceID();
|
||||
|
||||
// this fixes an issue where when we do a zone cancel what often ends up happening is we are sending
|
||||
// the client the wrong coordinates to zone back to. Often times it is the x,y,z of the destination zone
|
||||
// because we saved the destination x,y,z on the client profile before we rejected the zone request.
|
||||
// we're using rewind location because it should be where the client relatively was before we rejected the zone request.
|
||||
// it also prevents the client from getting caught up in a zone loop because if we sent them exactly back to where they
|
||||
// originated the request we could end up in a situation where the client is caught in a zone loop.
|
||||
m_Position.x = m_RewindLocation.x;
|
||||
m_Position.y = m_RewindLocation.y;
|
||||
m_Position.z = m_RewindLocation.z;
|
||||
zc2->x = m_Position.x;
|
||||
zc2->y = m_Position.y;
|
||||
zc2->z = m_Position.z;
|
||||
|
||||
LogZoning(
|
||||
"(zc2) Client [{}] char_name [{}] zoning to [{}] ({}) cancelled instance_id [{}] x [{}] y [{}] z [{}] zone_reason [{}] success [{}]",
|
||||
@ -425,6 +437,9 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
zc2->success
|
||||
);
|
||||
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
@ -740,10 +755,34 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
pZoneName = strcpy(new char[zd->long_name.length() + 1], zd->long_name.c_str());
|
||||
}
|
||||
|
||||
// If we are zoning to the same zone, we need to use the current instance ID if it is not specified.
|
||||
if (zoneID == zone->GetZoneID() && instance_id == 0) {
|
||||
instance_id = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
auto r = content_service.FindZone(zoneID, instance_id);
|
||||
if (r.zone_id) {
|
||||
zoneID = r.zone_id;
|
||||
instance_id = r.instance.id;
|
||||
LogZoning(
|
||||
"Client caught HandleZoneRoutingMiddleware [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
heading,
|
||||
ignorerestrictions,
|
||||
static_cast<int>(zm)
|
||||
);
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Client [{}] zone_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
"Client [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
@ -867,9 +906,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
else if(zm == ZoneSolicited || zm == ZoneToSafeCoords) {
|
||||
auto outapp =
|
||||
new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
|
||||
else if (zm == ZoneSolicited || zm == ZoneToSafeCoords) {
|
||||
auto outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
|
||||
RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer;
|
||||
|
||||
gmg->zone_id = zoneID;
|
||||
@ -880,6 +918,17 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
gmg->instance_id = instance_id;
|
||||
gmg->type = 0x01; //an observed value, not sure of meaning
|
||||
|
||||
LogZoning(
|
||||
"Player [{}] has requested zoning to zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
heading
|
||||
);
|
||||
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user