Implement bazaar item identity and offline trading rework

This commit is contained in:
Vayle
2026-03-19 22:18:32 -04:00
90 changed files with 5379 additions and 2165 deletions
+1
View File
@@ -9,6 +9,7 @@ set(world_sources
cli/cli_database_concurrency.cpp
cli/cli_database_dump.cpp
cli/cli_database_get_schema.cpp
cli/cli_database_item_unique_ids.cpp
cli/cli_database_set_account_status.cpp
cli/cli_database_updates.cpp
cli/cli_database_version.cpp
@@ -0,0 +1,54 @@
#include "world/world_server_cli.h"
#include "world/worlddb.h"
#include <cstdlib>
void WorldserverCLI::DatabaseItemUniqueIds(int argc, char **argv, argh::parser &cmd, std::string &description)
{
description = "Runs item_unique_id preflight, migration, and verification tasks";
std::vector<std::string> arguments = {};
std::vector<std::string> options = {
"--preflight",
"--migrate",
"--verify",
"--verbose",
"--keep-trading-state",
};
if (cmd[{"-h", "--help"}]) {
return;
}
EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv);
const bool verbose = cmd[{"-v", "--verbose"}];
const bool migrate = cmd[{"--migrate"}];
const bool verify = cmd[{"--verify"}];
const bool preflight = cmd[{"--preflight"}] || (!migrate && !verify);
const bool clear_trading_state = !cmd[{"--keep-trading-state"}];
bool success = true;
if (preflight) {
success = database.PreflightItemUniqueIdMigration(verbose) && success;
}
if (migrate) {
if (!database.MigrateItemUniqueIdData(clear_trading_state, verbose)) {
LogError("Item unique id migration failed verification");
success = false;
}
}
if (verify) {
if (!database.VerifyItemUniqueIdMigration(verbose)) {
LogError("Item unique id verification failed");
success = false;
}
}
if (!success) {
std::exit(1);
}
}
+1 -1
View File
@@ -2403,7 +2403,7 @@ bool Client::StoreCharacter(
e.ornament_icon = inst->GetOrnamentationIcon();
e.ornament_idfile = inst->GetOrnamentationIDFile();
e.ornament_hero_model = inst->GetOrnamentHeroModel();
e.guid = inst->GetSerialNumber();
e.item_unique_id = inst->GetUniqueID();
v.emplace_back(e);
}
+11
View File
@@ -202,6 +202,9 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
m_lfg = scl->LFG;
m_gm = scl->gm;
m_client_version = scl->ClientVersion;
m_trader = scl->trader;
m_buyer = scl->buyer;
m_offline = scl->offline;
// Fields from the LFG Window
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
@@ -219,6 +222,10 @@ void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
if (iZS != 0 && iZS != m_zone_server) {
return;
}
m_trader = false;
m_buyer = false;
m_offline = false;
SetOnline(iOnline);
SharedTaskManager::Instance()->RemoveActiveInvitationByCharacterID(CharID());
@@ -260,6 +267,10 @@ void ClientListEntry::ClearVars(bool iAll)
m_lfg = 0;
m_gm = 0;
m_client_version = 0;
m_trader = false;
m_buyer = false;
m_offline = false;
for (auto &elem: m_tell_queue) {
safe_delete_array(elem);
}
+30 -21
View File
@@ -13,7 +13,8 @@ typedef enum {
Online,
CharSelect,
Zoning,
InZone
InZone,
OfflineMode
} CLE_Status;
static const char *CLEStatusString[] = {
@@ -22,7 +23,8 @@ static const char *CLEStatusString[] = {
"Online",
"CharSelect",
"Zoning",
"InZone"
"InZone",
"OfflineMode"
};
class ZoneServer;
@@ -102,6 +104,10 @@ public:
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
inline const char *GetLFGComments() const { return m_lfg_comments; }
inline uint8 GetClientVersion() { return m_client_version; }
bool GetTrader() const { return m_trader; }
bool GetBuyer() const { return m_buyer; }
bool GetOfflineMode() const { return m_offline; }
void SetOfflineMode(bool status) { m_offline = status; }
inline bool TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
@@ -134,25 +140,28 @@ private:
// Character info
ZoneServer *m_zone_server{};
uint32 m_zone{};
uint16 m_instance{};
uint32 m_char_id{};
char m_char_name[64]{};
uint8 m_level{};
uint8 m_class_{};
uint16 m_race{};
uint8 m_anon{};
uint8 m_tells_off{};
uint32 m_guild_id{};
uint32 m_guild_rank;
bool m_guild_tribute_opt_in{};
bool m_lfg{};
uint8 m_gm{};
uint8 m_client_version{};
uint8 m_lfg_from_level{};
uint8 m_lfg_to_level{};
bool m_lfg_match_filter{};
char m_lfg_comments[64]{};
uint32 m_zone{};
uint16 m_instance{};
uint32 m_char_id{};
char m_char_name[64]{};
uint8 m_level{};
uint8 m_class_{};
uint16 m_race{};
uint8 m_anon{};
uint8 m_tells_off{};
uint32 m_guild_id{};
uint32 m_guild_rank;
bool m_guild_tribute_opt_in{};
bool m_lfg{};
uint8 m_gm{};
uint8 m_client_version{};
uint8 m_lfg_from_level{};
uint8 m_lfg_to_level{};
bool m_lfg_match_filter{};
char m_lfg_comments[64]{};
bool m_trader = false;
bool m_buyer = false;
bool m_offline = false;
// Tell Queue -- really a vector :D
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
+59 -22
View File
@@ -35,6 +35,7 @@
#include "world/zoneserver.h"
#include <set>
#include "../zone/string_ids.h"
uint32 numplayers = 0; //this really wants to be a member variable of ClientList...
@@ -427,7 +428,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
while (iterator.MoreElements()) {
if (iterator.GetData()->GetID() == scl->wid) {
cle = iterator.GetData();
if (scl->remove == 2) {
if (scl->remove == 3) {
cle->Update(zoneserver, scl, CLE_Status::OfflineMode);
}
else if (scl->remove == 2) {
cle->LeavingZone(zoneserver, CLE_Status::Offline);
}
else if (scl->remove == 1) {
@@ -441,7 +445,11 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
}
iterator.Advance();
}
if (scl->remove == 2) {
if (scl->remove == 3) {
cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::OfflineMode);
}
else if (scl->remove == 2) {
cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Online);
}
else if (scl->remove == 1) {
@@ -479,7 +487,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
" LFGFromLevel [{}]"
" LFGToLevel [{}]"
" LFGMatchFilter [{}]"
" LFGComments [{}]",
" LFGComments [{}]"
" Trader [{}]"
" Buyer [{}]"
" Offline [{}]",
scl->remove,
scl->wid,
scl->IP,
@@ -506,7 +517,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
scl->LFGFromLevel,
scl->LFGToLevel,
scl->LFGMatchFilter,
scl->LFGComments
scl->LFGComments,
scl->trader,
scl->buyer,
scl->offline
);
clientlist.Insert(cle);
@@ -784,7 +798,14 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
rankstring = 0;
iterator.Advance();
continue;
} else if (cle->GetGM()) {
}
else if (cle->GetTrader()) {
rankstring = TRADER;
}
else if (cle->GetBuyer()) {
rankstring = BUYER;
}
else if (cle->GetGM()) {
if (cle->Admin() >= AccountStatus::GMImpossible) {
rankstring = 5021;
} else if (cle->Admin() >= AccountStatus::GMMgmt) {
@@ -877,6 +898,18 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
strcpy(placcount,cle->AccountName());
}
if (cle->GetOfflineMode()) {
if (cle->GetTrader()) {
pidstring = 0x0430;
rankstring = 0xFFFFFFFF;
}
if (cle->GetBuyer()) {
pidstring = 0x0420;
rankstring = 0xFFFFFFFF;
}
}
memcpy(bufptr,&formatstring, sizeof(uint32));
bufptr+=sizeof(uint32);
memcpy(bufptr,&pidstring, sizeof(uint32));
@@ -1631,25 +1664,29 @@ void ClientList::OnTick(EQ::Timer *t)
outclient["Server"] = Json::Value();
}
outclient["CharID"] = cle->CharID();
outclient["name"] = cle->name();
outclient["zone"] = cle->zone();
outclient["instance"] = cle->instance();
outclient["level"] = cle->level();
outclient["class_"] = cle->class_();
outclient["race"] = cle->race();
outclient["Anon"] = cle->Anon();
outclient["CharID"] = cle->CharID();
outclient["name"] = cle->name();
outclient["zone"] = cle->zone();
outclient["instance"] = cle->instance();
outclient["level"] = cle->level();
outclient["class_"] = cle->class_();
outclient["race"] = cle->race();
outclient["Anon"] = cle->Anon();
outclient["TellsOff"] = cle->TellsOff();
outclient["GuildID"] = cle->GuildID();
outclient["LFG"] = cle->LFG();
outclient["GM"] = cle->GetGM();
outclient["LocalClient"] = cle->IsLocalClient();
outclient["LFGFromLevel"] = cle->GetLFGFromLevel();
outclient["LFGToLevel"] = cle->GetLFGToLevel();
outclient["TellsOff"] = cle->TellsOff();
outclient["GuildID"] = cle->GuildID();
outclient["LFG"] = cle->LFG();
outclient["GM"] = cle->GetGM();
outclient["LocalClient"] = cle->IsLocalClient();
outclient["LFGFromLevel"] = cle->GetLFGFromLevel();
outclient["LFGToLevel"] = cle->GetLFGToLevel();
outclient["LFGMatchFilter"] = cle->GetLFGMatchFilter();
outclient["LFGComments"] = cle->GetLFGComments();
outclient["ClientVersion"] = cle->GetClientVersion();
outclient["LFGComments"] = cle->GetLFGComments();
outclient["ClientVersion"] = cle->GetClientVersion();
outclient["Trader"] = cle->GetTrader();
outclient["Buyer"] = cle->GetBuyer();
outclient["OfflineMode"] = cle->GetOfflineMode();
out["data"].append(outclient);
Iterator.Advance();
+1 -1
View File
@@ -58,7 +58,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const
const std::string& account_name = database.GetAccountName(ret.account_id);
ret.account_name = account_name;
ret.status = database.GetAccountStatus(ret.account_id);
ret.status = database.GetAccountStatus(ret.account_id).status;
return ret;
}
+141 -11
View File
@@ -4,6 +4,11 @@
#include "common/eqemu_logsys.h"
#include "common/misc_functions.h"
#include "common/packet_dump.h"
#include "common/repositories/account_repository.h"
#include "common/repositories/buyer_repository.h"
#include "common/repositories/character_data_repository.h"
#include "common/repositories/offline_character_sessions_repository.h"
#include "common/repositories/trader_repository.h"
#include "common/servertalk.h"
#include "common/strings.h"
#include "common/version.h"
@@ -43,9 +48,9 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
const WorldConfig *Config = WorldConfig::get();
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
int16 status = database.GetAccountStatus(id);
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
int16 status = database.GetAccountStatus(id).status;
LogDebug(
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
@@ -123,14 +128,19 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
const WorldConfig *Config = WorldConfig::get();
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
int16 status = database.GetAccountStatus(id);
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
auto status_record = database.GetAccountStatus(id);
auto client = ClientList::Instance()->FindCLEByAccountID(id);
if (client) {
client->SetOfflineMode(status_record.offline);
}
LogDebug(
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
id,
status,
status_record.status,
utwr->lsaccountid,
utwr->worldid,
utwr->FromID,
@@ -152,7 +162,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
utwrs->response = UserToWorldStatusSuccess;
if (Config->Locked == true) {
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
if (status_record.status < (RuleI(GM, MinStatusToBypassLockedServer))) {
LogDebug(
"Server locked and status is not high enough for account_id [{0}]",
utwr->lsaccountid
@@ -164,27 +174,34 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
}
int32 x = Config->MaxClients;
if ((int32) numplayers >= x && x != -1 && x != 255 && status < (RuleI(GM, MinStatusToBypassLockedServer))) {
if ((int32) numplayers >= x && x != -1 && x != 255 && status_record.status < (RuleI(GM, MinStatusToBypassLockedServer))) {
LogDebug("World at capacity account_id [{0}]", utwr->lsaccountid);
utwrs->response = UserToWorldStatusWorldAtCapacity;
SendPacket(&outpack);
return;
}
if (status == -1) {
if (status_record.status == -1) {
LogDebug("User suspended account_id [{0}]", utwr->lsaccountid);
utwrs->response = UserToWorldStatusSuspended;
SendPacket(&outpack);
return;
}
if (status == -2) {
if (status_record.status == -2) {
LogDebug("User banned account_id [{0}]", utwr->lsaccountid);
utwrs->response = UserToWorldStatusBanned;
SendPacket(&outpack);
return;
}
if (status_record.offline || OfflineCharacterSessionsRepository::ExistsByAccountId(database, id)) {
LogDebug("User has an offline character for account_id [{0}]", utwr->lsaccountid);
utwrs->response = UserToWorldStatusOffilineTraderBuyer;
SendPacket(&outpack);
return;
}
if (RuleB(World, EnforceCharacterLimitAtLogin)) {
if (ClientList::Instance()->IsAccountInGame(utwr->lsaccountid)) {
LogDebug("User already online account_id [{0}]", utwr->lsaccountid);
@@ -572,6 +589,14 @@ bool LoginServer::Connect()
std::placeholders::_2
)
);
m_client->OnMessage(
ServerOP_UsertoWorldCancelOfflineRequest,
std::bind(
&LoginServer::ProcessUserToWorldCancelOfflineRequest,
this,
std::placeholders::_1,
std::placeholders::_2)
);
}
return true;
@@ -687,3 +712,108 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack)
}
}
void LoginServer::ProcessUserToWorldCancelOfflineRequest(uint16_t opcode, EQ::Net::Packet &p)
{
auto const Config = WorldConfig::get();
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
auto utwr = static_cast<UsertoWorldRequest *>(p.Data());
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
auto status_record = database.GetAccountStatus(id);
LogLoginserverDetail(
"Step 4 - World received CancelOfflineRequest for client login server account id {} offline mode {}",
id,
status_record.offline
);
LogDebug(
"id [{}] status [{}] account_id [{}] world_id [{}] ip [{}]",
id,
status_record.status,
utwr->lsaccountid,
utwr->worldid,
utwr->IPAddr
);
ServerPacket server_packet;
server_packet.size = sizeof(UsertoWorldResponse);
server_packet.pBuffer = new uchar[server_packet.size];
memset(server_packet.pBuffer, 0, server_packet.size);
auto utwrs = reinterpret_cast<UsertoWorldResponse *>(server_packet.pBuffer);
utwrs->lsaccountid = utwr->lsaccountid;
utwrs->ToID = utwr->FromID;
utwrs->worldid = utwr->worldid;
utwrs->response = UserToWorldStatusSuccess;
strn0cpy(utwrs->login, utwr->login, 64);
if (Config->Locked == true) {
if (status_record.status < RuleI(GM, MinStatusToBypassLockedServer)) {
LogDebug("Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid);
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineResponse;
utwrs->response = UserToWorldStatusWorldUnavail;
SendPacket(&server_packet);
return;
}
}
int32 x = Config->MaxClients;
if (static_cast<int32>(numplayers) >= x &&
x != -1 &&
x != 255 &&
status_record.status < RuleI(GM, MinStatusToBypassLockedServer)
) {
LogDebug("World at capacity account_id [{0}]", utwr->lsaccountid);
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineResponse;
utwrs->response = UserToWorldStatusWorldAtCapacity;
SendPacket(&server_packet);
return;
}
auto session = OfflineCharacterSessionsRepository::GetByAccountId(database, id);
auto trader = TraderRepository::GetAccountZoneIdAndInstanceIdByAccountId(database, id);
uint32 zone_id = session.id ? session.zone_id : trader.char_zone_id;
int32 instance_id = session.id ? session.instance_id : trader.char_zone_instance_id;
uint32 character_id = session.id ? session.character_id : trader.character_id;
if ((session.id || trader.id) &&
ZSList::Instance()->IsZoneBootedByZoneIdAndInstanceId(zone_id, instance_id)) {
LogLoginserverDetail(
"Step 5a(1) - World Checked offline users zone/instance is booted. "
"Sending packet to zone id {} instance id {}",
zone_id,
instance_id);
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineRequest;
ZSList::Instance()->SendPacketToBootedZones(&server_packet);
return;
}
LogLoginserverDetail("Step 5b(1) - World determined offline users zone/instance is not booted. Ignoring zone.");
LogLoginserverDetail("Step 5b(2) - World clearing users offline status from account table.");
database.TransactionBegin();
AccountRepository::SetOfflineStatus(database, id, false);
OfflineCharacterSessionsRepository::DeleteByAccountId(database, id);
LogLoginserverDetail("Step 5b(3) - World clearing trader and buyer tables.");
if (character_id) {
TraderRepository::DeleteWhere(database, fmt::format("`character_id` = '{}'", character_id));
BuyerRepository::DeleteBuyer(database, character_id);
}
auto commit_result = database.TransactionCommit();
if (!commit_result.Success()) {
database.TransactionRollback();
LogError(
"Failed clearing offline session state for account [{}]: ({}) {}",
id,
commit_result.ErrorNumber(),
commit_result.ErrorMessage()
);
utwrs->response = UserToWorldStatusWorldUnavail;
}
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineResponse;
SendPacket(&server_packet);
}
+1
View File
@@ -49,6 +49,7 @@ private:
void ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p);
void ProcessLSRemoteAddr(uint16_t opcode, EQ::Net::Packet &p);
void ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p);
void ProcessUserToWorldCancelOfflineRequest(uint16_t opcode, EQ::Net::Packet &p);
std::unique_ptr<EQ::Timer> m_keepalive;
-1
View File
@@ -634,4 +634,3 @@ void WorldBoot::SendDiscordMessage(int webhook_id, const std::string &message)
safe_delete(pack);
}
}
+1
View File
@@ -23,6 +23,7 @@ void WorldserverCLI::CommandHandler(int argc, char **argv)
function_map["database:schema"] = &WorldserverCLI::DatabaseGetSchema;
function_map["database:dump"] = &WorldserverCLI::DatabaseDump;
function_map["database:updates"] = &WorldserverCLI::DatabaseUpdates;
function_map["database:item-unique-ids"] = &WorldserverCLI::DatabaseItemUniqueIds;
function_map["test:test"] = &WorldserverCLI::TestCommand;
function_map["test:colors"] = &WorldserverCLI::TestColors;
function_map["test:expansion"] = &WorldserverCLI::ExpansionTestCommand;
+1
View File
@@ -17,6 +17,7 @@ public:
static void DatabaseGetSchema(int argc, char **argv, argh::parser &cmd, std::string &description);
static void DatabaseDump(int argc, char **argv, argh::parser &cmd, std::string &description);
static void DatabaseUpdates(int argc, char **argv, argh::parser &cmd, std::string &description);
static void DatabaseItemUniqueIds(int argc, char **argv, argh::parser &cmd, std::string &description);
static void TestCommand(int argc, char **argv, argh::parser &cmd, std::string &description);
static void TestColors(int argc, char **argv, argh::parser &cmd, std::string &description);
static void ExpansionTestCommand(int argc, char **argv, argh::parser &cmd, std::string &description);
+1 -1
View File
@@ -876,7 +876,7 @@ bool WorldDatabase::GetCharSelInventory(
continue;
}
EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges);
EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges, e.item_unique_id);
if (!inst) {
continue;
+12
View File
@@ -1019,3 +1019,15 @@ void ZSList::QueueServerReload(ServerReload::Type &type)
m_queued_reloads.emplace_back(type);
m_queued_reloads_mutex.unlock();
}
bool ZSList::IsZoneBootedByZoneIdAndInstanceId(uint32 zone_id, uint32 instance_id) const
{
for (auto const& z : zone_server_list) {
auto r = z.get();
if (r && r->GetZoneID() == zone_id && r->GetInstanceID() == instance_id) {
return true;
}
}
return false;
}
+1
View File
@@ -34,6 +34,7 @@ public:
bool SendPacketToZonesWithGMs(ServerPacket *pack);
bool SendPacketToBootedZones(ServerPacket* pack);
bool SetLockedZone(uint16 iZoneID, bool iLock);
bool IsZoneBootedByZoneIdAndInstanceId(uint32 zone_id, uint32 instance_id) const;
EQTime worldclock;
+43 -16
View File
@@ -1675,21 +1675,23 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
}
case ServerOP_BazaarPurchase: {
auto in = (BazaarPurchaseMessaging_Struct *)pack->pBuffer;
if (in->trader_buy_struct.trader_id <= 0) {
LogTrading(
"World Message <red>[{}] received with invalid trader_id <red>[{}]",
"ServerOP_BazaarPurchase",
in->trader_buy_struct.trader_id
);
return;
auto in = reinterpret_cast<BazaarPurchaseMessaging_Struct *>(pack->pBuffer);
switch (in->transaction_status) {
case BazaarPurchaseBuyerCompleteSendToSeller: {
ZSList::Instance()->SendPacket(in->trader_zone_id, in->trader_zone_instance_id, pack);
break;
}
case BazaarPurchaseTraderFailed:
case BazaarPurchaseSuccess: {
ZSList::Instance()->SendPacket(in->buyer_zone_id, in->buyer_zone_instance_id, pack);
break;
}
default: {
LogError(
"ServerOP_BazaarPurchase received with no corresponding action for [{}]",
in->transaction_status);
}
}
auto trader = ClientList::Instance()->FindCLEByCharacterID(in->trader_buy_struct.trader_id);
if (trader) {
ZSList::Instance()->SendPacket(trader->zone(), trader->instance(), pack);
}
break;
}
case ServerOP_BuyerMessaging: {
@@ -1725,9 +1727,34 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
}
default:
return;
default: {
break;
}
}
break;
}
case ServerOP_UsertoWorldCancelOfflineResponse: {
auto utwr = reinterpret_cast<UsertoWorldResponse *>(pack->pBuffer);
ServerPacket server_packet;
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineResponse;
server_packet.size = sizeof(UsertoWorldResponse);
server_packet.pBuffer = new uchar[server_packet.size];
memset(server_packet.pBuffer, 0, server_packet.size);
auto utwrs = reinterpret_cast<UsertoWorldResponse *>(server_packet.pBuffer);
utwrs->lsaccountid = utwr->lsaccountid;
utwrs->ToID = utwr->FromID;
utwrs->worldid = utwr->worldid;
utwrs->response = UserToWorldStatusSuccess;
strn0cpy(utwrs->login, utwr->login, 64);
LogLoginserverDetail(
"Step 7a - World received ServerOP_UsertoWorldCancelOfflineResponse back to login with success."
);
LoginServerList::Instance()->SendPacket(&server_packet);
break;
}
default: {
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);