mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-06 21:53:51 +00:00
Phase 1 Offline Trading
Cleanup and testing Zone updated builds ok World updated builds ok Update guild_base.h
This commit is contained in:
parent
a81ec11ea3
commit
cf3b9638c9
@ -186,6 +186,7 @@ SET(repositories
|
|||||||
repositories/base/base_character_leadership_abilities_repository.h
|
repositories/base/base_character_leadership_abilities_repository.h
|
||||||
repositories/base/base_character_material_repository.h
|
repositories/base/base_character_material_repository.h
|
||||||
repositories/base/base_character_memmed_spells_repository.h
|
repositories/base/base_character_memmed_spells_repository.h
|
||||||
|
repositories/base/base_character_offline_transactions_repository.h
|
||||||
repositories/base/base_character_parcels_repository.h
|
repositories/base/base_character_parcels_repository.h
|
||||||
repositories/base/base_character_parcels_containers_repository.h
|
repositories/base/base_character_parcels_containers_repository.h
|
||||||
repositories/base/base_character_peqzone_flags_repository.h
|
repositories/base/base_character_peqzone_flags_repository.h
|
||||||
@ -382,6 +383,7 @@ SET(repositories
|
|||||||
repositories/character_leadership_abilities_repository.h
|
repositories/character_leadership_abilities_repository.h
|
||||||
repositories/character_material_repository.h
|
repositories/character_material_repository.h
|
||||||
repositories/character_memmed_spells_repository.h
|
repositories/character_memmed_spells_repository.h
|
||||||
|
repositories/character_offline_transactions_repository.h
|
||||||
repositories/character_parcels_repository.h
|
repositories/character_parcels_repository.h
|
||||||
repositories/character_parcels_containers_repository.h
|
repositories/character_parcels_containers_repository.h
|
||||||
repositories/character_peqzone_flags_repository.h
|
repositories/character_peqzone_flags_repository.h
|
||||||
|
|||||||
@ -210,7 +210,7 @@ void Database::LoginIP(uint32 account_id, const std::string& login_ip)
|
|||||||
QueryDatabase(query);
|
QueryDatabase(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16 Database::GetAccountStatus(uint32 account_id)
|
AccountStatus::StatusRecord Database::GetAccountStatus(uint32 account_id)
|
||||||
{
|
{
|
||||||
auto e = AccountRepository::FindOne(*this, account_id);
|
auto e = AccountRepository::FindOne(*this, account_id);
|
||||||
|
|
||||||
@ -222,7 +222,11 @@ int16 Database::GetAccountStatus(uint32 account_id)
|
|||||||
AccountRepository::UpdateOne(*this, e);
|
AccountRepository::UpdateOne(*this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.status;
|
AccountStatus::StatusRecord result{};
|
||||||
|
result.status = e.status;
|
||||||
|
result.offline = e.offline;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Database::CreateAccount(
|
uint32 Database::CreateAccount(
|
||||||
@ -2265,6 +2269,7 @@ void Database::ClearGuildOnlineStatus()
|
|||||||
void Database::ClearTraderDetails()
|
void Database::ClearTraderDetails()
|
||||||
{
|
{
|
||||||
TraderRepository::Truncate(*this);
|
TraderRepository::Truncate(*this);
|
||||||
|
AccountRepository::ClearAllOfflineStatus(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::ClearBuyerDetails()
|
void Database::ClearBuyerDetails()
|
||||||
|
|||||||
@ -172,8 +172,8 @@ public:
|
|||||||
const std::string GetLiveChar(uint32 account_id);
|
const std::string GetLiveChar(uint32 account_id);
|
||||||
bool SetAccountStatus(const std::string& account_name, int16 status);
|
bool SetAccountStatus(const std::string& account_name, int16 status);
|
||||||
bool SetLocalPassword(uint32 account_id, const std::string& password);
|
bool SetLocalPassword(uint32 account_id, const std::string& password);
|
||||||
|
AccountStatus::StatusRecord GetAccountStatus(uint32 account_id);
|
||||||
bool UpdateLiveChar(const std::string& name, uint32 account_id);
|
bool UpdateLiveChar(const std::string& name, uint32 account_id);
|
||||||
int16 GetAccountStatus(uint32 account_id);
|
|
||||||
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
|
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
|
||||||
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
|
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
|
||||||
uint32 CreateAccount(
|
uint32 CreateAccount(
|
||||||
|
|||||||
@ -7240,6 +7240,32 @@ ALTER TABLE `trader`
|
|||||||
)",
|
)",
|
||||||
.content_schema_update = false
|
.content_schema_update = false
|
||||||
},
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9325,
|
||||||
|
.description = "2025_01_27_offline_account_status.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `account` LIKE 'offline'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `account`
|
||||||
|
ADD COLUMN `offline` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `time_creation`;
|
||||||
|
|
||||||
|
CREATE TABLE `character_offline_transactions` (
|
||||||
|
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`character_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`type` INT(10) UNSIGNED NULL DEFAULT '0',
|
||||||
|
`item_name` VARCHAR(64) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
|
||||||
|
`quantity` INT(11) NULL DEFAULT '0',
|
||||||
|
`price` BIGINT(20) UNSIGNED NULL DEFAULT '0',
|
||||||
|
`buyer_name` VARCHAR(64) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `idx_character_id` (`character_id`)
|
||||||
|
)
|
||||||
|
COLLATE='latin1_swedish_ci'
|
||||||
|
ENGINE=InnoDB;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
// -- template; copy/paste this when you need to create a new entry
|
// -- template; copy/paste this when you need to create a new entry
|
||||||
// ManifestEntry{
|
// ManifestEntry{
|
||||||
// .version = 9228,
|
// .version = 9228,
|
||||||
|
|||||||
@ -59,6 +59,7 @@ namespace DatabaseSchema {
|
|||||||
{"character_leadership_abilities", "id"},
|
{"character_leadership_abilities", "id"},
|
||||||
{"character_material", "id"},
|
{"character_material", "id"},
|
||||||
{"character_memmed_spells", "id"},
|
{"character_memmed_spells", "id"},
|
||||||
|
{"character_offline_transactions", "character_id"},
|
||||||
{"character_parcels", "char_id"},
|
{"character_parcels", "char_id"},
|
||||||
{"character_parcels_containers", "id"},
|
{"character_parcels_containers", "id"},
|
||||||
{"character_pet_buffs", "char_id"},
|
{"character_pet_buffs", "char_id"},
|
||||||
@ -134,6 +135,7 @@ namespace DatabaseSchema {
|
|||||||
"character_leadership_abilities",
|
"character_leadership_abilities",
|
||||||
"character_material",
|
"character_material",
|
||||||
"character_memmed_spells",
|
"character_memmed_spells",
|
||||||
|
"character_offline_transactions",
|
||||||
"character_parcels",
|
"character_parcels",
|
||||||
"character_parcels_containers",
|
"character_parcels_containers",
|
||||||
"character_pet_buffs",
|
"character_pet_buffs",
|
||||||
|
|||||||
@ -46,6 +46,11 @@ namespace AccountStatus {
|
|||||||
constexpr uint8 Max = 255;
|
constexpr uint8 Max = 255;
|
||||||
|
|
||||||
std::string GetName(uint8 account_status);
|
std::string GetName(uint8 account_status);
|
||||||
|
|
||||||
|
struct StatusRecord {
|
||||||
|
int16 status;
|
||||||
|
uint32 offline;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<uint8, std::string> account_status_names = {
|
static std::map<uint8, std::string> account_status_names = {
|
||||||
|
|||||||
@ -71,6 +71,8 @@ N(OP_BuyerItems),
|
|||||||
N(OP_CameraEffect),
|
N(OP_CameraEffect),
|
||||||
N(OP_Camp),
|
N(OP_Camp),
|
||||||
N(OP_CancelSneakHide),
|
N(OP_CancelSneakHide),
|
||||||
|
N(OP_CancelOfflineTrader),
|
||||||
|
N(OP_CancelOfflineTraderResponse),
|
||||||
N(OP_CancelTask),
|
N(OP_CancelTask),
|
||||||
N(OP_CancelTrade),
|
N(OP_CancelTrade),
|
||||||
N(OP_CashReward),
|
N(OP_CashReward),
|
||||||
@ -381,6 +383,7 @@ N(OP_MultiLineMsg),
|
|||||||
N(OP_NewSpawn),
|
N(OP_NewSpawn),
|
||||||
N(OP_NewTitlesAvailable),
|
N(OP_NewTitlesAvailable),
|
||||||
N(OP_NewZone),
|
N(OP_NewZone),
|
||||||
|
N(OP_Offline),
|
||||||
N(OP_OnLevelMessage),
|
N(OP_OnLevelMessage),
|
||||||
N(OP_OpenContainer),
|
N(OP_OpenContainer),
|
||||||
N(OP_OpenDiscordMerchant),
|
N(OP_OpenDiscordMerchant),
|
||||||
|
|||||||
@ -326,6 +326,7 @@ union
|
|||||||
bool buyer;
|
bool buyer;
|
||||||
bool untargetable;
|
bool untargetable;
|
||||||
uint32 npc_tint_id;
|
uint32 npc_tint_id;
|
||||||
|
bool offline;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PlayerState_Struct {
|
struct PlayerState_Struct {
|
||||||
@ -3913,12 +3914,12 @@ struct SimpleMessage_Struct{
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GuildMemberUpdate_Struct {
|
struct GuildMemberUpdate_Struct {
|
||||||
/*00*/ uint32 GuildID;
|
/*00*/ uint32 GuildID;
|
||||||
/*04*/ char MemberName[64];
|
/*04*/ char MemberName[64];
|
||||||
/*68*/ uint16 ZoneID;
|
/*68*/ uint16 ZoneID;
|
||||||
/*70*/ uint16 InstanceID; //speculated
|
/*72*/ uint16 InstanceID; //speculated
|
||||||
/*72*/ uint32 LastSeen; //unix timestamp
|
/*76*/ uint32 LastSeen; //unix timestamp
|
||||||
/*76*/
|
/*80*/ uint32 offline_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GuildMemberLevelUpdate_Struct {
|
struct GuildMemberLevelUpdate_Struct {
|
||||||
@ -3941,6 +3942,7 @@ struct Internal_GuildMemberEntry_Struct {
|
|||||||
uint16 zoneinstance; //network byte order
|
uint16 zoneinstance; //network byte order
|
||||||
uint16 zone_id; //network byte order
|
uint16 zone_id; //network byte order
|
||||||
uint32 online;
|
uint32 online;
|
||||||
|
uint32 offline_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding.
|
struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding.
|
||||||
|
|||||||
@ -1098,6 +1098,7 @@ namespace PlayerEvent {
|
|||||||
int32 charges;
|
int32 charges;
|
||||||
uint64 total_cost;
|
uint64 total_cost;
|
||||||
uint64 player_money_balance;
|
uint64 player_money_balance;
|
||||||
|
bool offline_purchase;
|
||||||
|
|
||||||
// cereal
|
// cereal
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
@ -1153,7 +1154,8 @@ namespace PlayerEvent {
|
|||||||
CEREAL_NVP(quantity),
|
CEREAL_NVP(quantity),
|
||||||
CEREAL_NVP(charges),
|
CEREAL_NVP(charges),
|
||||||
CEREAL_NVP(total_cost),
|
CEREAL_NVP(total_cost),
|
||||||
CEREAL_NVP(player_money_balance)
|
CEREAL_NVP(player_money_balance),
|
||||||
|
CEREAL_NVP(offline_purchase)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1174,7 +1176,9 @@ namespace PlayerEvent {
|
|||||||
int32 charges;
|
int32 charges;
|
||||||
uint64 total_cost;
|
uint64 total_cost;
|
||||||
uint64 player_money_balance;
|
uint64 player_money_balance;
|
||||||
|
bool offline_purchase;
|
||||||
|
|
||||||
|
// cereal
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar)
|
void serialize(Archive& ar)
|
||||||
{
|
{
|
||||||
@ -1228,7 +1232,8 @@ namespace PlayerEvent {
|
|||||||
CEREAL_NVP(quantity),
|
CEREAL_NVP(quantity),
|
||||||
CEREAL_NVP(charges),
|
CEREAL_NVP(charges),
|
||||||
CEREAL_NVP(total_cost),
|
CEREAL_NVP(total_cost),
|
||||||
CEREAL_NVP(player_money_balance)
|
CEREAL_NVP(player_money_balance),
|
||||||
|
CEREAL_NVP(offline_purchase)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -844,8 +844,10 @@ bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg)
|
|||||||
#define GuildMemberBaseQuery \
|
#define GuildMemberBaseQuery \
|
||||||
"SELECT c.`id`, c.`name`, c.`class`, c.`level`, c.`last_login`, c.`zone_id`," \
|
"SELECT c.`id`, c.`name`, c.`class`, c.`level`, c.`last_login`, c.`zone_id`," \
|
||||||
" g.`guild_id`, g.`rank`, g.`tribute_enable`, g.`total_tribute`, g.`last_tribute`," \
|
" g.`guild_id`, g.`rank`, g.`tribute_enable`, g.`total_tribute`, g.`last_tribute`," \
|
||||||
" g.`banker`, g.`public_note`, g.`alt`, g.`online` " \
|
" g.`banker`, g.`public_note`, g.`alt`, g.`online`, a.`offline` " \
|
||||||
" FROM `character_data` AS c LEFT JOIN `guild_members` AS g ON c.`id` = g.`char_id` "
|
" FROM `character_data` AS c LEFT JOIN `guild_members` AS g ON c.`id` = g.`char_id` " \
|
||||||
|
" LEFT JOIN `account` AS a ON a.`id` = c.`account_id` "
|
||||||
|
|
||||||
static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into)
|
static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into)
|
||||||
{
|
{
|
||||||
//fields from `characer_`
|
//fields from `characer_`
|
||||||
@ -866,6 +868,7 @@ static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into)
|
|||||||
into.public_note = row[12] ? row[12] : "";
|
into.public_note = row[12] ? row[12] : "";
|
||||||
into.alt = row[13] ? (row[13][0] == '0' ? false : true) : false;
|
into.alt = row[13] ? (row[13][0] == '0' ? false : true) : false;
|
||||||
into.online = row[14] ? (row[14][0] == '0' ? false : true) : false;
|
into.online = row[14] ? (row[14][0] == '0' ? false : true) : false;
|
||||||
|
into.offline_mode = row[15] ? (row[15][0] == '0' ? false : true) : false;
|
||||||
|
|
||||||
//a little sanity checking/cleanup
|
//a little sanity checking/cleanup
|
||||||
if (into.guild_id == 0) {
|
if (into.guild_id == 0) {
|
||||||
|
|||||||
@ -55,16 +55,17 @@ class CharGuildInfo
|
|||||||
uint32 time_last_on;
|
uint32 time_last_on;
|
||||||
uint32 zone_id;
|
uint32 zone_id;
|
||||||
|
|
||||||
//fields from `guild_members`
|
// fields from `guild_members`
|
||||||
uint32 guild_id;
|
uint32 guild_id;
|
||||||
uint8 rank;
|
uint8 rank;
|
||||||
bool tribute_enable;
|
bool tribute_enable;
|
||||||
uint32 total_tribute;
|
uint32 total_tribute;
|
||||||
uint32 last_tribute; //timestamp
|
uint32 last_tribute; // timestamp
|
||||||
bool banker;
|
bool banker;
|
||||||
bool alt;
|
bool alt;
|
||||||
std::string public_note;
|
std::string public_note;
|
||||||
bool online;
|
bool online;
|
||||||
|
bool offline_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
//this object holds guild functionality shared between world and zone.
|
//this object holds guild functionality shared between world and zone.
|
||||||
|
|||||||
@ -1845,7 +1845,7 @@ namespace RoF2
|
|||||||
e->zoneinstance = 0;
|
e->zoneinstance = 0;
|
||||||
e->zone_id = htons(emu_e->zone_id);
|
e->zone_id = htons(emu_e->zone_id);
|
||||||
e->unknown_one2 = htonl(1);
|
e->unknown_one2 = htonl(1);
|
||||||
e->unknown04 = 0;
|
e->offline_mode = htonl(emu_e->offline_mode);
|
||||||
|
|
||||||
#undef SlideStructString
|
#undef SlideStructString
|
||||||
#undef PutFieldN
|
#undef PutFieldN
|
||||||
@ -1862,14 +1862,12 @@ namespace RoF2
|
|||||||
{
|
{
|
||||||
SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct);
|
SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct);
|
||||||
|
|
||||||
OUT(GuildID);
|
eq->guild_id = emu->GuildID;
|
||||||
memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName));
|
eq->last_seen = emu->LastSeen;
|
||||||
//OUT(ZoneID);
|
eq->instance_id = emu->InstanceID;
|
||||||
//OUT(InstanceID);
|
eq->zone_id = emu->ZoneID;
|
||||||
eq->InstanceID = emu->InstanceID;
|
eq->offline_mode = emu->offline_mode;
|
||||||
eq->ZoneID = emu->ZoneID;
|
memcpy(eq->member_name, emu->MemberName, sizeof(eq->member_name));
|
||||||
OUT(LastSeen);
|
|
||||||
eq->Unknown76 = 0;
|
|
||||||
|
|
||||||
FINISH_ENCODE();
|
FINISH_ENCODE();
|
||||||
}
|
}
|
||||||
@ -4475,6 +4473,7 @@ namespace RoF2
|
|||||||
*p = nullptr;
|
*p = nullptr;
|
||||||
|
|
||||||
char *InBuffer = (char *)in->pBuffer;
|
char *InBuffer = (char *)in->pBuffer;
|
||||||
|
std::vector<uint32> p_ids { 0x430, 0x420 };
|
||||||
|
|
||||||
WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer;
|
WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer;
|
||||||
|
|
||||||
@ -4500,8 +4499,9 @@ namespace RoF2
|
|||||||
x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
|
x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x);
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x);
|
||||||
|
|
||||||
InBuffer += 4;
|
x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, std::ranges::find(p_ids.begin(), p_ids.end(), x) == p_ids.end() ? 0 : x);
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff);
|
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff);
|
||||||
|
|
||||||
char Name[64];
|
char Name[64];
|
||||||
@ -4737,6 +4737,10 @@ namespace RoF2
|
|||||||
OtherData = OtherData | 0x01;
|
OtherData = OtherData | 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (emu->offline) {
|
||||||
|
OtherData = OtherData | 0x02;
|
||||||
|
}
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
||||||
// float EmitterScalingRadius
|
// float EmitterScalingRadius
|
||||||
|
|
||||||
|
|||||||
@ -3681,22 +3681,22 @@ struct SimpleMessage_Struct{
|
|||||||
// Size: 52 + strings
|
// Size: 52 + strings
|
||||||
// Other than the strings, all of this packet is network byte order (reverse from normal)
|
// Other than the strings, all of this packet is network byte order (reverse from normal)
|
||||||
struct GuildMemberEntry_Struct {
|
struct GuildMemberEntry_Struct {
|
||||||
char name[1]; // variable length
|
char name[1]; // variable length
|
||||||
uint32 level;
|
uint32 level;
|
||||||
uint32 banker; // 1=yes, 0=no
|
uint32 banker; // 1=yes, 0=no
|
||||||
uint32 class_;
|
uint32 class_;
|
||||||
uint32 rank;
|
uint32 rank;
|
||||||
uint32 time_last_on;
|
uint32 time_last_on;
|
||||||
uint32 tribute_enable;
|
uint32 tribute_enable;
|
||||||
uint32 unknown01; // Seen 0
|
uint32 unknown01; // Seen 0
|
||||||
uint32 total_tribute; // total guild tribute donated, network byte order
|
uint32 total_tribute; // total guild tribute donated, network byte order
|
||||||
uint32 last_tribute; // unix timestamp
|
uint32 last_tribute; // unix timestamp
|
||||||
uint32 unknown_one; // unknown, set to 1
|
uint32 unknown_one; // unknown, set to 1
|
||||||
char public_note[1]; // variable length.
|
char public_note[1]; // variable length.
|
||||||
uint16 zoneinstance; // Seen 0s or -1 in RoF2
|
uint16 zoneinstance; // Seen 0s or -1 in RoF2
|
||||||
uint16 zone_id; // Seen 0s or -1 in RoF2
|
uint16 zone_id; // Seen 0s or -1 in RoF2
|
||||||
uint32 unknown_one2; // unknown, set to 1
|
uint32 unknown_one2; // unknown, set to 1
|
||||||
uint32 unknown04; // Seen 0
|
uint32 offline_mode; // Displays OFFLINE MODE instead of Zone Name
|
||||||
};
|
};
|
||||||
|
|
||||||
//just for display purposes, this is not actually used in the message encoding other than for size.
|
//just for display purposes, this is not actually used in the message encoding other than for size.
|
||||||
@ -3731,13 +3731,12 @@ struct GuildStatus_Struct
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GuildMemberUpdate_Struct {
|
struct GuildMemberUpdate_Struct {
|
||||||
/*00*/ uint32 GuildID;
|
/*00*/ uint32 guild_id;
|
||||||
/*04*/ char MemberName[64];
|
/*04*/ char member_name[64];
|
||||||
/*68*/ uint16 ZoneID;
|
/*68*/ uint16 zone_id;
|
||||||
/*70*/ uint16 InstanceID; //speculated
|
/*70*/ uint16 instance_id; //speculated
|
||||||
/*72*/ uint32 LastSeen; //unix timestamp
|
/*72*/ uint32 last_seen; //unix timestamp
|
||||||
/*76*/ uint32 Unknown76;
|
/*76*/ uint32 offline_mode;
|
||||||
/*80*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GuildMemberLevelUpdate_Struct {
|
struct GuildMemberLevelUpdate_Struct {
|
||||||
|
|||||||
@ -107,6 +107,45 @@ public:
|
|||||||
|
|
||||||
return AccountRepository::UpdateOne(db, e);
|
return AccountRepository::UpdateOne(db, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetOfflineStatus(Database& db, const uint32 account_id, bool offline_status)
|
||||||
|
{
|
||||||
|
auto account = FindOne(db, account_id);
|
||||||
|
if (!account.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
account.offline = offline_status;
|
||||||
|
UpdateOne(db, account);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearAllOfflineStatus(Database& db)
|
||||||
|
{
|
||||||
|
auto query = fmt::format("UPDATE {} SET `offline` = '0' WHERE `offline` = '1';",
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
|
||||||
|
db.QueryDatabase(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetAllOfflineStatus(Database& db, const uint32 character_id)
|
||||||
|
{
|
||||||
|
auto query = fmt::format("SELECT a.`offline` "
|
||||||
|
"FROM `account` AS a "
|
||||||
|
"INNER JOIN character_data AS c ON c.account_id = a.id "
|
||||||
|
"WHERE c.id = '{}'",
|
||||||
|
character_id
|
||||||
|
);
|
||||||
|
auto results = db.QueryDatabase(query);
|
||||||
|
if (!results.Success() || !results.RowCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
bool const status = static_cast<int16>(Strings::ToInt(row[0]));
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_ACCOUNT_REPOSITORY_H
|
#endif //EQEMU_ACCOUNT_REPOSITORY_H
|
||||||
|
|||||||
@ -39,6 +39,7 @@ public:
|
|||||||
uint8_t rulesflag;
|
uint8_t rulesflag;
|
||||||
time_t suspendeduntil;
|
time_t suspendeduntil;
|
||||||
uint32_t time_creation;
|
uint32_t time_creation;
|
||||||
|
uint8_t offline;
|
||||||
std::string ban_reason;
|
std::string ban_reason;
|
||||||
std::string suspend_reason;
|
std::string suspend_reason;
|
||||||
std::string crc_eqgame;
|
std::string crc_eqgame;
|
||||||
@ -74,6 +75,7 @@ public:
|
|||||||
"rulesflag",
|
"rulesflag",
|
||||||
"suspendeduntil",
|
"suspendeduntil",
|
||||||
"time_creation",
|
"time_creation",
|
||||||
|
"offline",
|
||||||
"ban_reason",
|
"ban_reason",
|
||||||
"suspend_reason",
|
"suspend_reason",
|
||||||
"crc_eqgame",
|
"crc_eqgame",
|
||||||
@ -105,6 +107,7 @@ public:
|
|||||||
"rulesflag",
|
"rulesflag",
|
||||||
"UNIX_TIMESTAMP(suspendeduntil)",
|
"UNIX_TIMESTAMP(suspendeduntil)",
|
||||||
"time_creation",
|
"time_creation",
|
||||||
|
"offline",
|
||||||
"ban_reason",
|
"ban_reason",
|
||||||
"suspend_reason",
|
"suspend_reason",
|
||||||
"crc_eqgame",
|
"crc_eqgame",
|
||||||
@ -170,6 +173,7 @@ public:
|
|||||||
e.rulesflag = 0;
|
e.rulesflag = 0;
|
||||||
e.suspendeduntil = 0;
|
e.suspendeduntil = 0;
|
||||||
e.time_creation = 0;
|
e.time_creation = 0;
|
||||||
|
e.offline = 0;
|
||||||
e.ban_reason = "";
|
e.ban_reason = "";
|
||||||
e.suspend_reason = "";
|
e.suspend_reason = "";
|
||||||
e.crc_eqgame = "";
|
e.crc_eqgame = "";
|
||||||
@ -231,11 +235,12 @@ public:
|
|||||||
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||||
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
||||||
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||||
e.ban_reason = row[20] ? row[20] : "";
|
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||||
e.suspend_reason = row[21] ? row[21] : "";
|
e.ban_reason = row[21] ? row[21] : "";
|
||||||
e.crc_eqgame = row[22] ? row[22] : "";
|
e.suspend_reason = row[22] ? row[22] : "";
|
||||||
e.crc_skillcaps = row[23] ? row[23] : "";
|
e.crc_eqgame = row[23] ? row[23] : "";
|
||||||
e.crc_basedata = row[24] ? row[24] : "";
|
e.crc_skillcaps = row[24] ? row[24] : "";
|
||||||
|
e.crc_basedata = row[25] ? row[25] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@ -288,11 +293,12 @@ public:
|
|||||||
v.push_back(columns[17] + " = " + std::to_string(e.rulesflag));
|
v.push_back(columns[17] + " = " + std::to_string(e.rulesflag));
|
||||||
v.push_back(columns[18] + " = FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
v.push_back(columns[18] + " = FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
||||||
v.push_back(columns[19] + " = " + std::to_string(e.time_creation));
|
v.push_back(columns[19] + " = " + std::to_string(e.time_creation));
|
||||||
v.push_back(columns[20] + " = '" + Strings::Escape(e.ban_reason) + "'");
|
v.push_back(columns[20] + " = " + std::to_string(e.offline));
|
||||||
v.push_back(columns[21] + " = '" + Strings::Escape(e.suspend_reason) + "'");
|
v.push_back(columns[21] + " = '" + Strings::Escape(e.ban_reason) + "'");
|
||||||
v.push_back(columns[22] + " = '" + Strings::Escape(e.crc_eqgame) + "'");
|
v.push_back(columns[22] + " = '" + Strings::Escape(e.suspend_reason) + "'");
|
||||||
v.push_back(columns[23] + " = '" + Strings::Escape(e.crc_skillcaps) + "'");
|
v.push_back(columns[23] + " = '" + Strings::Escape(e.crc_eqgame) + "'");
|
||||||
v.push_back(columns[24] + " = '" + Strings::Escape(e.crc_basedata) + "'");
|
v.push_back(columns[24] + " = '" + Strings::Escape(e.crc_skillcaps) + "'");
|
||||||
|
v.push_back(columns[25] + " = '" + Strings::Escape(e.crc_basedata) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@ -334,6 +340,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.rulesflag));
|
v.push_back(std::to_string(e.rulesflag));
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
||||||
v.push_back(std::to_string(e.time_creation));
|
v.push_back(std::to_string(e.time_creation));
|
||||||
|
v.push_back(std::to_string(e.offline));
|
||||||
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
||||||
@ -388,6 +395,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.rulesflag));
|
v.push_back(std::to_string(e.rulesflag));
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
||||||
v.push_back(std::to_string(e.time_creation));
|
v.push_back(std::to_string(e.time_creation));
|
||||||
|
v.push_back(std::to_string(e.offline));
|
||||||
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
||||||
@ -446,11 +454,12 @@ public:
|
|||||||
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||||
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
||||||
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||||
e.ban_reason = row[20] ? row[20] : "";
|
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||||
e.suspend_reason = row[21] ? row[21] : "";
|
e.ban_reason = row[21] ? row[21] : "";
|
||||||
e.crc_eqgame = row[22] ? row[22] : "";
|
e.suspend_reason = row[22] ? row[22] : "";
|
||||||
e.crc_skillcaps = row[23] ? row[23] : "";
|
e.crc_eqgame = row[23] ? row[23] : "";
|
||||||
e.crc_basedata = row[24] ? row[24] : "";
|
e.crc_skillcaps = row[24] ? row[24] : "";
|
||||||
|
e.crc_basedata = row[25] ? row[25] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@ -495,11 +504,12 @@ public:
|
|||||||
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
e.rulesflag = row[17] ? static_cast<uint8_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||||
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
e.suspendeduntil = strtoll(row[18] ? row[18] : "-1", nullptr, 10);
|
||||||
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
e.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||||
e.ban_reason = row[20] ? row[20] : "";
|
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||||
e.suspend_reason = row[21] ? row[21] : "";
|
e.ban_reason = row[21] ? row[21] : "";
|
||||||
e.crc_eqgame = row[22] ? row[22] : "";
|
e.suspend_reason = row[22] ? row[22] : "";
|
||||||
e.crc_skillcaps = row[23] ? row[23] : "";
|
e.crc_eqgame = row[23] ? row[23] : "";
|
||||||
e.crc_basedata = row[24] ? row[24] : "";
|
e.crc_skillcaps = row[24] ? row[24] : "";
|
||||||
|
e.crc_basedata = row[25] ? row[25] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@ -594,6 +604,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.rulesflag));
|
v.push_back(std::to_string(e.rulesflag));
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
||||||
v.push_back(std::to_string(e.time_creation));
|
v.push_back(std::to_string(e.time_creation));
|
||||||
|
v.push_back(std::to_string(e.offline));
|
||||||
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
||||||
@ -641,6 +652,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.rulesflag));
|
v.push_back(std::to_string(e.rulesflag));
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
|
||||||
v.push_back(std::to_string(e.time_creation));
|
v.push_back(std::to_string(e.time_creation));
|
||||||
|
v.push_back(std::to_string(e.offline));
|
||||||
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
|
||||||
|
|||||||
@ -0,0 +1,451 @@
|
|||||||
|
/**
|
||||||
|
* DO NOT MODIFY THIS FILE
|
||||||
|
*
|
||||||
|
* This repository was automatically generated and is NOT to be modified directly.
|
||||||
|
* Any repository modifications are meant to be made to the repository extending the base.
|
||||||
|
* Any modifications to base repositories are to be made by the generator only
|
||||||
|
*
|
||||||
|
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||||
|
* @docs https://docs.eqemu.io/developer/repositories
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_BASE_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
|
#define EQEMU_BASE_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../../database.h"
|
||||||
|
#include "../../strings.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
class BaseCharacterOfflineTransactionsRepository {
|
||||||
|
public:
|
||||||
|
struct CharacterOfflineTransactions {
|
||||||
|
uint64_t id;
|
||||||
|
uint32_t character_id;
|
||||||
|
uint32_t type;
|
||||||
|
std::string item_name;
|
||||||
|
int32_t quantity;
|
||||||
|
uint64_t price;
|
||||||
|
std::string buyer_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string PrimaryKey()
|
||||||
|
{
|
||||||
|
return std::string("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"character_id",
|
||||||
|
"type",
|
||||||
|
"item_name",
|
||||||
|
"quantity",
|
||||||
|
"price",
|
||||||
|
"buyer_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SelectColumns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"character_id",
|
||||||
|
"type",
|
||||||
|
"item_name",
|
||||||
|
"quantity",
|
||||||
|
"price",
|
||||||
|
"buyer_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SelectColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("character_offline_transactions");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
SelectColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseInsert()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"INSERT INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterOfflineTransactions NewEntity()
|
||||||
|
{
|
||||||
|
CharacterOfflineTransactions e{};
|
||||||
|
|
||||||
|
e.id = 0;
|
||||||
|
e.character_id = 0;
|
||||||
|
e.type = 0;
|
||||||
|
e.item_name = "";
|
||||||
|
e.quantity = 0;
|
||||||
|
e.price = 0;
|
||||||
|
e.buyer_name = "";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterOfflineTransactions GetCharacterOfflineTransactions(
|
||||||
|
const std::vector<CharacterOfflineTransactions> &character_offline_transactionss,
|
||||||
|
int character_offline_transactions_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &character_offline_transactions : character_offline_transactionss) {
|
||||||
|
if (character_offline_transactions.id == character_offline_transactions_id) {
|
||||||
|
return character_offline_transactions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterOfflineTransactions FindOne(
|
||||||
|
Database& db,
|
||||||
|
int character_offline_transactions_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
|
character_offline_transactions_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
CharacterOfflineTransactions e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.type = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[3] ? row[3] : "";
|
||||||
|
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.price = row[5] ? strtoull(row[5], nullptr, 10) : 0;
|
||||||
|
e.buyer_name = row[6] ? row[6] : "";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteOne(
|
||||||
|
Database& db,
|
||||||
|
int character_offline_transactions_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey(),
|
||||||
|
character_offline_transactions_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateOne(
|
||||||
|
Database& db,
|
||||||
|
const CharacterOfflineTransactions &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto columns = Columns();
|
||||||
|
|
||||||
|
v.push_back(columns[1] + " = " + std::to_string(e.character_id));
|
||||||
|
v.push_back(columns[2] + " = " + std::to_string(e.type));
|
||||||
|
v.push_back(columns[3] + " = '" + Strings::Escape(e.item_name) + "'");
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.quantity));
|
||||||
|
v.push_back(columns[5] + " = " + std::to_string(e.price));
|
||||||
|
v.push_back(columns[6] + " = '" + Strings::Escape(e.buyer_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"UPDATE {} SET {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
Strings::Implode(", ", v),
|
||||||
|
PrimaryKey(),
|
||||||
|
e.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CharacterOfflineTransactions InsertOne(
|
||||||
|
Database& db,
|
||||||
|
CharacterOfflineTransactions e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.character_id));
|
||||||
|
v.push_back(std::to_string(e.type));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
v.push_back(std::to_string(e.quantity));
|
||||||
|
v.push_back(std::to_string(e.price));
|
||||||
|
v.push_back("'" + Strings::Escape(e.buyer_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.Success()) {
|
||||||
|
e.id = results.LastInsertedID();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = NewEntity();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<CharacterOfflineTransactions> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.character_id));
|
||||||
|
v.push_back(std::to_string(e.type));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
v.push_back(std::to_string(e.quantity));
|
||||||
|
v.push_back(std::to_string(e.price));
|
||||||
|
v.push_back("'" + Strings::Escape(e.buyer_name) + "'");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<CharacterOfflineTransactions> All(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<CharacterOfflineTransactions> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{}",
|
||||||
|
BaseSelect()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
CharacterOfflineTransactions e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.type = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[3] ? row[3] : "";
|
||||||
|
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.price = row[5] ? strtoull(row[5], nullptr, 10) : 0;
|
||||||
|
e.buyer_name = row[6] ? row[6] : "";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<CharacterOfflineTransactions> GetWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
std::vector<CharacterOfflineTransactions> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {}",
|
||||||
|
BaseSelect(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
CharacterOfflineTransactions e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.character_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.type = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[3] ? row[3] : "";
|
||||||
|
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.price = row[5] ? strtoull(row[5], nullptr, 10) : 0;
|
||||||
|
e.buyer_name = row[6] ? row[6] : "";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {}",
|
||||||
|
TableName(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Truncate(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"TRUNCATE TABLE {}",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 GetMaxId(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||||
|
PrimaryKey(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COUNT(*) FROM {} {}",
|
||||||
|
TableName(),
|
||||||
|
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseReplace()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"REPLACE INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceOne(
|
||||||
|
Database& db,
|
||||||
|
const CharacterOfflineTransactions &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.character_id));
|
||||||
|
v.push_back(std::to_string(e.type));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
v.push_back(std::to_string(e.quantity));
|
||||||
|
v.push_back(std::to_string(e.price));
|
||||||
|
v.push_back("'" + Strings::Escape(e.buyer_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<CharacterOfflineTransactions> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.character_id));
|
||||||
|
v.push_back(std::to_string(e.type));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
v.push_back(std::to_string(e.quantity));
|
||||||
|
v.push_back(std::to_string(e.price));
|
||||||
|
v.push_back("'" + Strings::Escape(e.buyer_name) + "'");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_BASE_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
@ -106,8 +106,13 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buy_lines =
|
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
|
||||||
BaseBuyerBuyLinesRepository::GetWhere(db, fmt::format("`buyer_id` = {}", buyer.front().id));
|
db,
|
||||||
|
fmt::format("`buyer_id` = '{}'", buyer.front().id)
|
||||||
|
);
|
||||||
|
if (buy_lines.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> buy_line_ids{};
|
std::vector<std::string> buy_line_ids{};
|
||||||
for (auto const &bl: buy_lines) {
|
for (auto const &bl: buy_lines) {
|
||||||
@ -175,6 +180,26 @@ public:
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool UpdateBuyerEntityID(Database &db, uint32 char_id, uint32 old_entity_id, uint32 new_entity_id)
|
||||||
|
{
|
||||||
|
if (!char_id || !old_entity_id || !new_entity_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto results = GetWhere(db, fmt::format("`char_id` = '{}' AND `char_entity_id` = '{}' LIMIT 1;", char_id, old_entity_id));
|
||||||
|
|
||||||
|
if (results.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &e: results) {
|
||||||
|
e.char_entity_id = new_entity_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceMany(db, results);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_BUYER_REPOSITORY_H
|
#endif //EQEMU_BUYER_REPOSITORY_H
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef EQEMU_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
|
#define EQEMU_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../strings.h"
|
||||||
|
#include "base/base_character_offline_transactions_repository.h"
|
||||||
|
|
||||||
|
class CharacterOfflineTransactionsRepository: public BaseCharacterOfflineTransactionsRepository {
|
||||||
|
public:
|
||||||
|
|
||||||
|
#define TRADER_TRANSACTION 1
|
||||||
|
#define BUYER_TRANSACTION 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file was auto generated and can be modified and extended upon
|
||||||
|
*
|
||||||
|
* Base repository methods are automatically
|
||||||
|
* generated in the "base" version of this repository. The base repository
|
||||||
|
* is immutable and to be left untouched, while methods in this class
|
||||||
|
* are used as extension methods for more specific persistence-layer
|
||||||
|
* accessors or mutators.
|
||||||
|
*
|
||||||
|
* Base Methods (Subject to be expanded upon in time)
|
||||||
|
*
|
||||||
|
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||||
|
*
|
||||||
|
* InsertOne
|
||||||
|
* UpdateOne
|
||||||
|
* DeleteOne
|
||||||
|
* FindOne
|
||||||
|
* GetWhere(std::string where_filter)
|
||||||
|
* DeleteWhere(std::string where_filter)
|
||||||
|
* InsertMany
|
||||||
|
* All
|
||||||
|
*
|
||||||
|
* Example custom methods in a repository
|
||||||
|
*
|
||||||
|
* CharacterOfflineTransactionsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||||
|
* CharacterOfflineTransactionsRepository::GetWhereNeverExpires()
|
||||||
|
* CharacterOfflineTransactionsRepository::GetWhereXAndY()
|
||||||
|
* CharacterOfflineTransactionsRepository::DeleteWhereXAndY()
|
||||||
|
*
|
||||||
|
* Most of the above could be covered by base methods, but if you as a developer
|
||||||
|
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||||
|
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||||
|
* method and encapsulate filters there
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Custom extended repository methods here
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_CHARACTER_OFFLINE_TRANSACTIONS_REPOSITORY_H
|
||||||
@ -395,6 +395,49 @@ public:
|
|||||||
|
|
||||||
return all_entries;
|
return all_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Trader GetAccountZoneIdAndInstanceIdByAccountId(Database &db, uint32 account_id)
|
||||||
|
{
|
||||||
|
auto trader_query = fmt::format(
|
||||||
|
"SELECT t.id, t.char_id, t.char_zone_id, t.char_zone_instance_id "
|
||||||
|
"FROM trader AS t "
|
||||||
|
"WHERE t.char_id IN(SELECT c.id FROM character_data AS c WHERE c.account_id = '{}') "
|
||||||
|
"LIMIT 1;",
|
||||||
|
account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
auto buyer_query = fmt::format(
|
||||||
|
"SELECT t.id, t.char_id, t.char_zone_id, t.char_zone_instance_id "
|
||||||
|
"FROM buyer AS t "
|
||||||
|
"WHERE t.char_id IN(SELECT c.id FROM character_data AS c WHERE c.account_id = '{}') "
|
||||||
|
"LIMIT 1;",
|
||||||
|
account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
Trader e{};
|
||||||
|
|
||||||
|
auto trader_results = db.QueryDatabase(trader_query);
|
||||||
|
auto buyer_results = db.QueryDatabase(buyer_query);
|
||||||
|
if (trader_results.RowCount() == 0 && buyer_results.RowCount() == 0) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
MySQLRequestRow row;
|
||||||
|
if (trader_results.RowCount() > 0) {
|
||||||
|
row = trader_results.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buyer_results.RowCount() > 0) {
|
||||||
|
row = buyer_results.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.char_zone_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.char_zone_instance_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_TRADER_REPOSITORY_H
|
#endif //EQEMU_TRADER_REPOSITORY_H
|
||||||
|
|||||||
@ -229,10 +229,12 @@
|
|||||||
#define ServerOP_LSPlayerJoinWorld 0x3007
|
#define ServerOP_LSPlayerJoinWorld 0x3007
|
||||||
#define ServerOP_LSPlayerZoneChange 0x3008
|
#define ServerOP_LSPlayerZoneChange 0x3008
|
||||||
|
|
||||||
#define ServerOP_UsertoWorldReqLeg 0xAB00
|
#define ServerOP_UsertoWorldReqLeg 0xAB00
|
||||||
#define ServerOP_UsertoWorldRespLeg 0xAB01
|
#define ServerOP_UsertoWorldRespLeg 0xAB01
|
||||||
#define ServerOP_UsertoWorldReq 0xAB02
|
#define ServerOP_UsertoWorldReq 0xAB02
|
||||||
#define ServerOP_UsertoWorldResp 0xAB03
|
#define ServerOP_UsertoWorldResp 0xAB03
|
||||||
|
#define ServerOP_UsertoWorldCancelOfflineRequest 0xAB04
|
||||||
|
#define ServerOP_UsertoWorldCancelOfflineResponse 0xAB05
|
||||||
|
|
||||||
#define ServerOP_LauncherConnectInfo 0x3000
|
#define ServerOP_LauncherConnectInfo 0x3000
|
||||||
#define ServerOP_LauncherZoneRequest 0x3001
|
#define ServerOP_LauncherZoneRequest 0x3001
|
||||||
@ -360,12 +362,13 @@ enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_
|
|||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
UserToWorldStatusWorldUnavail = 0,
|
UserToWorldStatusWorldUnavail = 0,
|
||||||
UserToWorldStatusSuccess = 1,
|
UserToWorldStatusSuccess = 1,
|
||||||
UserToWorldStatusSuspended = -1,
|
UserToWorldStatusSuspended = -1,
|
||||||
UserToWorldStatusBanned = -2,
|
UserToWorldStatusBanned = -2,
|
||||||
UserToWorldStatusWorldAtCapacity = -3,
|
UserToWorldStatusWorldAtCapacity = -3,
|
||||||
UserToWorldStatusAlreadyOnline = -4
|
UserToWorldStatusAlreadyOnline = -4,
|
||||||
|
UserToWorldStatusOffilineTraderBuyer = -5
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -567,6 +570,9 @@ struct ServerClientList_Struct {
|
|||||||
uint8 LFGToLevel;
|
uint8 LFGToLevel;
|
||||||
bool LFGMatchFilter;
|
bool LFGMatchFilter;
|
||||||
char LFGComments[64];
|
char LFGComments[64];
|
||||||
|
bool trader;
|
||||||
|
bool buyer;
|
||||||
|
bool offline;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ServerClientListKeepAlive_Struct {
|
struct ServerClientListKeepAlive_Struct {
|
||||||
@ -1030,6 +1036,7 @@ struct ServerGuildMemberUpdate_Struct {
|
|||||||
char member_name[64];
|
char member_name[64];
|
||||||
uint32 zone_id;
|
uint32 zone_id;
|
||||||
uint32 last_seen;
|
uint32 last_seen;
|
||||||
|
uint32 offline_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ServerGuildPermissionUpdate_Struct {
|
struct ServerGuildPermissionUpdate_Struct {
|
||||||
|
|||||||
@ -71,6 +71,25 @@ bool Client::Process()
|
|||||||
SendPlayToWorld((const char *) app->pBuffer);
|
SendPlayToWorld((const char *) app->pBuffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CancelOfflineTrader: {
|
||||||
|
if (app->Size() < sizeof(CancelOfflineTrader)) {
|
||||||
|
LogError("Play received but it is too small, discarding");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_delete_array(app->pBuffer);
|
||||||
|
auto buffer = new unsigned char[sizeof(PlayEverquestRequest)];
|
||||||
|
auto data = (PlayEverquestRequest *) buffer;
|
||||||
|
data->base_header.sequence = GetCurrentPlaySequence();
|
||||||
|
data->server_number = GetSelectedPlayServerID();
|
||||||
|
app->pBuffer = buffer;
|
||||||
|
app->size = sizeof(PlayEverquestRequest);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 1 - Hit CancelOfflineTrader Mode Packet for.");
|
||||||
|
SendCancelOfflineStatusToWorld((const char *) app->pBuffer);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete app;
|
delete app;
|
||||||
@ -561,3 +580,27 @@ std::string Client::GetClientLoggingDescription()
|
|||||||
client_ip
|
client_ip
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::SendCancelOfflineStatusToWorld(const char *data)
|
||||||
|
{
|
||||||
|
if (m_client_status != cs_logged_in) {
|
||||||
|
LogError("Client sent a play request when they were not logged in, discarding");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto *play = (const PlayEverquestRequest *) data;
|
||||||
|
auto server_id_in = (unsigned int) play->server_number;
|
||||||
|
auto sequence_in = (unsigned int) play->base_header.sequence;
|
||||||
|
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 2 - Cancel Offline Status Request received from client [{}] server number [{}] sequence [{}]",
|
||||||
|
GetAccountName(),
|
||||||
|
server_id_in,
|
||||||
|
sequence_in
|
||||||
|
);
|
||||||
|
|
||||||
|
m_selected_play_server_id = (unsigned int) play->server_number;
|
||||||
|
m_play_sequence_id = sequence_in;
|
||||||
|
m_selected_play_server_id = server_id_in;
|
||||||
|
server.server_manager->SendUserToWorldCancelOfflineRequest(server_id_in, m_account_id, m_loginserver_name);
|
||||||
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ public:
|
|||||||
void SendPlayToWorld(const char *data);
|
void SendPlayToWorld(const char *data);
|
||||||
void SendServerListPacket(uint32 seq);
|
void SendServerListPacket(uint32 seq);
|
||||||
void SendPlayResponse(EQApplicationPacket *outapp);
|
void SendPlayResponse(EQApplicationPacket *outapp);
|
||||||
|
void SendCancelOfflineStatusToWorld(const char *data);
|
||||||
void GenerateRandomLoginKey();
|
void GenerateRandomLoginKey();
|
||||||
unsigned int GetAccountID() const { return m_account_id; }
|
unsigned int GetAccountID() const { return m_account_id; }
|
||||||
std::string GetLoginServerName() const { return m_loginserver_name; }
|
std::string GetLoginServerName() const { return m_loginserver_name; }
|
||||||
|
|||||||
@ -79,6 +79,11 @@ struct PlayEverquestResponse {
|
|||||||
uint32 server_number;
|
uint32 server_number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CancelOfflineTrader {
|
||||||
|
LoginBaseMessage base_header;
|
||||||
|
int16_t unk;
|
||||||
|
};
|
||||||
|
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
enum LSClientVersion {
|
enum LSClientVersion {
|
||||||
@ -154,11 +159,12 @@ namespace LS {
|
|||||||
constexpr static int ERROR_NONE = 101; // No Error
|
constexpr static int ERROR_NONE = 101; // No Error
|
||||||
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
|
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
|
||||||
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
|
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
|
||||||
|
constexpr static int ERROR_OFFLINE_TRADER = 114; // You have a character logged into a world server as an OFFLINE TRADER from this account. You may only have 1 character from a single account logged into a server at a time (even across different servers). Would you like to remove this character from the game so you may login?
|
||||||
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
|
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
|
||||||
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
|
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
|
||||||
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
|
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
|
||||||
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
|
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -11,3 +11,5 @@ OP_Poll=0x0029
|
|||||||
OP_LoginExpansionPacketData=0x0031
|
OP_LoginExpansionPacketData=0x0031
|
||||||
OP_EnterChat=0x000f
|
OP_EnterChat=0x000f
|
||||||
OP_PollResponse=0x0011
|
OP_PollResponse=0x0011
|
||||||
|
OP_CancelOfflineTrader=0x0016
|
||||||
|
OP_CancelOfflineTraderResponse=0x0030
|
||||||
@ -51,6 +51,12 @@ WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> wo
|
|||||||
ServerOP_LSAccountUpdate,
|
ServerOP_LSAccountUpdate,
|
||||||
std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
|
std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
worldserver_connection->OnMessage(
|
||||||
|
ServerOP_UsertoWorldCancelOfflineResponse,
|
||||||
|
std::bind(
|
||||||
|
&WorldServer::ProcessUserToWorldCancelOfflineResponse, this, std::placeholders::_1, std::placeholders::_2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldServer::~WorldServer() = default;
|
WorldServer::~WorldServer() = default;
|
||||||
@ -298,6 +304,10 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
case UserToWorldStatusAlreadyOnline:
|
case UserToWorldStatusAlreadyOnline:
|
||||||
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
||||||
break;
|
break;
|
||||||
|
case UserToWorldStatusOffilineTraderBuyer:
|
||||||
|
r->base_reply.success = false;
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_OFFLINE_TRADER;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
r->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
||||||
break;
|
break;
|
||||||
@ -774,3 +784,113 @@ void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
|
|||||||
|
|
||||||
strn0cpy(name, server_long_name.c_str(), 201);
|
strn0cpy(name, server_long_name.c_str(), 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldServer::ProcessUserToWorldCancelOfflineResponse(uint16_t opcode, const EQ::Net::Packet &packet)
|
||||||
|
{
|
||||||
|
LogNetcode(
|
||||||
|
"Application packet received from server [{:#04x}] [Size: {}]\n{}",
|
||||||
|
opcode,
|
||||||
|
packet.Length(),
|
||||||
|
packet.ToString()
|
||||||
|
);
|
||||||
|
LogLoginserverDetail("Step 8 - back in Login Server from world.");
|
||||||
|
|
||||||
|
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
||||||
|
LogError(
|
||||||
|
"Received application packet from server that had opcode ServerOP_UsertoWorldCancelOfflineResp, "
|
||||||
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = (UsertoWorldResponse *) packet.Data();
|
||||||
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
|
|
||||||
|
Client *c = server.client_manager->GetClient(
|
||||||
|
res->lsaccountid,
|
||||||
|
res->login
|
||||||
|
);
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
LogDebug(
|
||||||
|
"Found client with user id of [{}] and account name of {}",
|
||||||
|
res->lsaccountid,
|
||||||
|
c->GetAccountName().c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
auto client_packet = EQApplicationPacket(OP_CancelOfflineTraderResponse, sizeof(PlayEverquestResponse));
|
||||||
|
auto client_packet_payload = reinterpret_cast<PlayEverquestResponse *>(client_packet.pBuffer);
|
||||||
|
|
||||||
|
client_packet_payload->base_header.sequence = c->GetCurrentPlaySequence();
|
||||||
|
client_packet_payload->server_number = c->GetSelectedPlayServerID();
|
||||||
|
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 9 - Send Play Response OPCODE 30 to remove the client message about having an offline Trader/Buyer"
|
||||||
|
);
|
||||||
|
c->SendPlayResponse(&client_packet);
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket(OP_PlayEverquestResponse, sizeof(PlayEverquestResponse));
|
||||||
|
auto r = reinterpret_cast<PlayEverquestResponse *>(outapp->pBuffer);
|
||||||
|
r->base_header.sequence = c->GetCurrentPlaySequence();
|
||||||
|
r->server_number = c->GetSelectedPlayServerID();
|
||||||
|
|
||||||
|
LogDebug(
|
||||||
|
"Found sequence and play of [{}] [{}]",
|
||||||
|
c->GetCurrentPlaySequence(),
|
||||||
|
c->GetSelectedPlayServerID()
|
||||||
|
);
|
||||||
|
|
||||||
|
//LogDebug("[Size: [{}]] {}", outapp->size, DumpPacketToString(outapp));
|
||||||
|
|
||||||
|
if (res->response > 0) {
|
||||||
|
r->base_reply.success = true;
|
||||||
|
SendClientAuthToWorld(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (res->response) {
|
||||||
|
case UserToWorldStatusSuccess:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusWorldUnavail:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusSuspended:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusBanned:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusWorldAtCapacity:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusAlreadyOnline:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
||||||
|
break;
|
||||||
|
case UserToWorldStatusOffilineTraderBuyer:
|
||||||
|
r->base_reply.success = false;
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_OFFLINE_TRADER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebug(
|
||||||
|
"Sending play response with following data, allowed [{}], sequence {}, server number {}, message {}",
|
||||||
|
r->base_reply.success,
|
||||||
|
r->base_header.sequence,
|
||||||
|
r->server_number,
|
||||||
|
r->base_reply.error_str_id
|
||||||
|
);
|
||||||
|
LogLoginserverDetail("Step 10 - Send Play Response EnterWorld to client");
|
||||||
|
|
||||||
|
c->SendPlayResponse(outapp);
|
||||||
|
delete outapp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogError(
|
||||||
|
"Received User-To-World Response for [{}] but could not find the client referenced!.",
|
||||||
|
res->lsaccountid
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -60,6 +60,7 @@ private:
|
|||||||
void ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet);
|
void ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||||
void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet);
|
void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||||
void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet);
|
void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||||
|
void ProcessUserToWorldCancelOfflineResponse(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||||
|
|
||||||
std::shared_ptr<EQ::Net::ServertalkServerConnection> m_connection;
|
std::shared_ptr<EQ::Net::ServertalkServerConnection> m_connection;
|
||||||
|
|
||||||
|
|||||||
@ -216,3 +216,35 @@ const std::list<std::unique_ptr<WorldServer>> &WorldServerManager::GetWorldServe
|
|||||||
{
|
{
|
||||||
return m_world_servers;
|
return m_world_servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldServerManager::SendUserToWorldCancelOfflineRequest(
|
||||||
|
unsigned int server_id,
|
||||||
|
unsigned int client_account_id,
|
||||||
|
const std::string &client_loginserver
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto iter = std::find_if(
|
||||||
|
m_world_servers.begin(), m_world_servers.end(),
|
||||||
|
[&](const std::unique_ptr<WorldServer> &server) {
|
||||||
|
return server->GetServerId() == server_id;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (iter != m_world_servers.end()) {
|
||||||
|
EQ::Net::DynamicPacket outapp;
|
||||||
|
outapp.Resize(sizeof(UsertoWorldRequest));
|
||||||
|
|
||||||
|
auto *r = reinterpret_cast<UsertoWorldRequest *>(outapp.Data());
|
||||||
|
r->worldid = server_id;
|
||||||
|
r->lsaccountid = client_account_id;
|
||||||
|
strncpy(r->login, client_loginserver.c_str(), 64);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 3 - Sending ServerOP_UsertoWorldCancelOfflineRequest to world for client account id {}", client_account_id);
|
||||||
|
(*iter)->GetConnection()->Send(ServerOP_UsertoWorldCancelOfflineRequest, outapp);
|
||||||
|
|
||||||
|
LogNetcode("[UsertoWorldRequest] [Size: {}]\n{}", outapp.Length(), outapp.ToString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogError("Client requested a user to world but supplied an invalid id of {}", server_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,11 @@ public:
|
|||||||
unsigned int client_account_id,
|
unsigned int client_account_id,
|
||||||
const std::string &client_loginserver
|
const std::string &client_loginserver
|
||||||
);
|
);
|
||||||
|
void SendUserToWorldCancelOfflineRequest(
|
||||||
|
unsigned int server_id,
|
||||||
|
unsigned int client_account_id,
|
||||||
|
const std::string &client_loginserver
|
||||||
|
);
|
||||||
std::unique_ptr<EQApplicationPacket> CreateServerListPacket(Client *client, uint32 sequence);
|
std::unique_ptr<EQApplicationPacket> CreateServerListPacket(Client *client, uint32 sequence);
|
||||||
bool DoesServerExist(const std::string &s, const std::string &server_short_name, WorldServer *ignore = nullptr);
|
bool DoesServerExist(const std::string &s, const std::string &server_short_name, WorldServer *ignore = nullptr);
|
||||||
void DestroyServerByName(std::string s, std::string server_short_name, WorldServer *ignore = nullptr);
|
void DestroyServerByName(std::string s, std::string server_short_name, WorldServer *ignore = nullptr);
|
||||||
|
|||||||
@ -749,3 +749,6 @@ OP_ChangePetName=0x5dab
|
|||||||
|
|
||||||
OP_InvokeNameChangeImmediate=0x4fe2
|
OP_InvokeNameChangeImmediate=0x4fe2
|
||||||
OP_InvokeNameChangeLazy=0x2f2e
|
OP_InvokeNameChangeLazy=0x2f2e
|
||||||
|
|
||||||
|
#Offline Trading Mode
|
||||||
|
OP_Offline=0x53d3
|
||||||
@ -202,6 +202,9 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
|
|||||||
m_lfg = scl->LFG;
|
m_lfg = scl->LFG;
|
||||||
m_gm = scl->gm;
|
m_gm = scl->gm;
|
||||||
m_client_version = scl->ClientVersion;
|
m_client_version = scl->ClientVersion;
|
||||||
|
m_trader = scl->trader;
|
||||||
|
m_buyer = scl->buyer;
|
||||||
|
m_offline = scl->offline;
|
||||||
|
|
||||||
// Fields from the LFG Window
|
// Fields from the LFG Window
|
||||||
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
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) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_trader = false;
|
||||||
|
m_buyer = false;
|
||||||
|
m_offline = false;
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
|
|
||||||
SharedTaskManager::Instance()->RemoveActiveInvitationByCharacterID(CharID());
|
SharedTaskManager::Instance()->RemoveActiveInvitationByCharacterID(CharID());
|
||||||
@ -260,6 +267,10 @@ void ClientListEntry::ClearVars(bool iAll)
|
|||||||
m_lfg = 0;
|
m_lfg = 0;
|
||||||
m_gm = 0;
|
m_gm = 0;
|
||||||
m_client_version = 0;
|
m_client_version = 0;
|
||||||
|
m_trader = false;
|
||||||
|
m_buyer = false;
|
||||||
|
m_offline = false;
|
||||||
|
|
||||||
for (auto &elem: m_tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,8 @@ typedef enum {
|
|||||||
Online,
|
Online,
|
||||||
CharSelect,
|
CharSelect,
|
||||||
Zoning,
|
Zoning,
|
||||||
InZone
|
InZone,
|
||||||
|
OfflineMode
|
||||||
} CLE_Status;
|
} CLE_Status;
|
||||||
|
|
||||||
static const char *CLEStatusString[] = {
|
static const char *CLEStatusString[] = {
|
||||||
@ -23,7 +24,8 @@ static const char *CLEStatusString[] = {
|
|||||||
"Online",
|
"Online",
|
||||||
"CharSelect",
|
"CharSelect",
|
||||||
"Zoning",
|
"Zoning",
|
||||||
"InZone"
|
"InZone",
|
||||||
|
"OfflineMode"
|
||||||
};
|
};
|
||||||
|
|
||||||
class ZoneServer;
|
class ZoneServer;
|
||||||
@ -103,6 +105,10 @@ public:
|
|||||||
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
||||||
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
||||||
inline uint8 GetClientVersion() { return m_client_version; }
|
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 TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||||
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
||||||
@ -135,25 +141,28 @@ private:
|
|||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
ZoneServer *m_zone_server{};
|
ZoneServer *m_zone_server{};
|
||||||
uint32 m_zone{};
|
uint32 m_zone{};
|
||||||
uint16 m_instance{};
|
uint16 m_instance{};
|
||||||
uint32 m_char_id{};
|
uint32 m_char_id{};
|
||||||
char m_char_name[64]{};
|
char m_char_name[64]{};
|
||||||
uint8 m_level{};
|
uint8 m_level{};
|
||||||
uint8 m_class_{};
|
uint8 m_class_{};
|
||||||
uint16 m_race{};
|
uint16 m_race{};
|
||||||
uint8 m_anon{};
|
uint8 m_anon{};
|
||||||
uint8 m_tells_off{};
|
uint8 m_tells_off{};
|
||||||
uint32 m_guild_id{};
|
uint32 m_guild_id{};
|
||||||
uint32 m_guild_rank;
|
uint32 m_guild_rank;
|
||||||
bool m_guild_tribute_opt_in{};
|
bool m_guild_tribute_opt_in{};
|
||||||
bool m_lfg{};
|
bool m_lfg{};
|
||||||
uint8 m_gm{};
|
uint8 m_gm{};
|
||||||
uint8 m_client_version{};
|
uint8 m_client_version{};
|
||||||
uint8 m_lfg_from_level{};
|
uint8 m_lfg_from_level{};
|
||||||
uint8 m_lfg_to_level{};
|
uint8 m_lfg_to_level{};
|
||||||
bool m_lfg_match_filter{};
|
bool m_lfg_match_filter{};
|
||||||
char m_lfg_comments[64]{};
|
char m_lfg_comments[64]{};
|
||||||
|
bool m_trader = false;
|
||||||
|
bool m_buyer = false;
|
||||||
|
bool m_offline = false;
|
||||||
|
|
||||||
// Tell Queue -- really a vector :D
|
// Tell Queue -- really a vector :D
|
||||||
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
|
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
|
||||||
|
|||||||
@ -427,7 +427,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
while (iterator.MoreElements()) {
|
while (iterator.MoreElements()) {
|
||||||
if (iterator.GetData()->GetID() == scl->wid) {
|
if (iterator.GetData()->GetID() == scl->wid) {
|
||||||
cle = iterator.GetData();
|
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);
|
cle->LeavingZone(zoneserver, CLE_Status::Offline);
|
||||||
}
|
}
|
||||||
else if (scl->remove == 1) {
|
else if (scl->remove == 1) {
|
||||||
@ -441,7 +444,11 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
}
|
}
|
||||||
iterator.Advance();
|
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);
|
cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status::Online);
|
||||||
}
|
}
|
||||||
else if (scl->remove == 1) {
|
else if (scl->remove == 1) {
|
||||||
@ -479,7 +486,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
" LFGFromLevel [{}]"
|
" LFGFromLevel [{}]"
|
||||||
" LFGToLevel [{}]"
|
" LFGToLevel [{}]"
|
||||||
" LFGMatchFilter [{}]"
|
" LFGMatchFilter [{}]"
|
||||||
" LFGComments [{}]",
|
" LFGComments [{}]"
|
||||||
|
" Trader [{}]"
|
||||||
|
" Buyer [{}]"
|
||||||
|
" Offline [{}]",
|
||||||
scl->remove,
|
scl->remove,
|
||||||
scl->wid,
|
scl->wid,
|
||||||
scl->IP,
|
scl->IP,
|
||||||
@ -506,7 +516,10 @@ void ClientList::ClientUpdate(ZoneServer *zoneserver, ServerClientList_Struct *s
|
|||||||
scl->LFGFromLevel,
|
scl->LFGFromLevel,
|
||||||
scl->LFGToLevel,
|
scl->LFGToLevel,
|
||||||
scl->LFGMatchFilter,
|
scl->LFGMatchFilter,
|
||||||
scl->LFGComments
|
scl->LFGComments,
|
||||||
|
scl->trader,
|
||||||
|
scl->buyer,
|
||||||
|
scl->offline
|
||||||
);
|
);
|
||||||
|
|
||||||
clientlist.Insert(cle);
|
clientlist.Insert(cle);
|
||||||
@ -784,7 +797,14 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
|
|||||||
rankstring = 0;
|
rankstring = 0;
|
||||||
iterator.Advance();
|
iterator.Advance();
|
||||||
continue;
|
continue;
|
||||||
} else if (cle->GetGM()) {
|
}
|
||||||
|
else if (cle->GetTrader()) {
|
||||||
|
rankstring = 12315;
|
||||||
|
}
|
||||||
|
else if (cle->GetBuyer()) {
|
||||||
|
rankstring = 6056;
|
||||||
|
}
|
||||||
|
else if (cle->GetGM()) {
|
||||||
if (cle->Admin() >= AccountStatus::GMImpossible) {
|
if (cle->Admin() >= AccountStatus::GMImpossible) {
|
||||||
rankstring = 5021;
|
rankstring = 5021;
|
||||||
} else if (cle->Admin() >= AccountStatus::GMMgmt) {
|
} else if (cle->Admin() >= AccountStatus::GMMgmt) {
|
||||||
@ -877,6 +897,18 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
|
|||||||
strcpy(placcount,cle->AccountName());
|
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));
|
memcpy(bufptr,&formatstring, sizeof(uint32));
|
||||||
bufptr+=sizeof(uint32);
|
bufptr+=sizeof(uint32);
|
||||||
memcpy(bufptr,&pidstring, sizeof(uint32));
|
memcpy(bufptr,&pidstring, sizeof(uint32));
|
||||||
@ -1631,25 +1663,29 @@ void ClientList::OnTick(EQ::Timer *t)
|
|||||||
outclient["Server"] = Json::Value();
|
outclient["Server"] = Json::Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
outclient["CharID"] = cle->CharID();
|
outclient["CharID"] = cle->CharID();
|
||||||
outclient["name"] = cle->name();
|
outclient["name"] = cle->name();
|
||||||
outclient["zone"] = cle->zone();
|
outclient["zone"] = cle->zone();
|
||||||
outclient["instance"] = cle->instance();
|
outclient["instance"] = cle->instance();
|
||||||
outclient["level"] = cle->level();
|
outclient["level"] = cle->level();
|
||||||
outclient["class_"] = cle->class_();
|
outclient["class_"] = cle->class_();
|
||||||
outclient["race"] = cle->race();
|
outclient["race"] = cle->race();
|
||||||
outclient["Anon"] = cle->Anon();
|
outclient["Anon"] = cle->Anon();
|
||||||
|
|
||||||
outclient["TellsOff"] = cle->TellsOff();
|
outclient["TellsOff"] = cle->TellsOff();
|
||||||
outclient["GuildID"] = cle->GuildID();
|
outclient["GuildID"] = cle->GuildID();
|
||||||
outclient["LFG"] = cle->LFG();
|
outclient["LFG"] = cle->LFG();
|
||||||
outclient["GM"] = cle->GetGM();
|
outclient["GM"] = cle->GetGM();
|
||||||
outclient["LocalClient"] = cle->IsLocalClient();
|
outclient["LocalClient"] = cle->IsLocalClient();
|
||||||
outclient["LFGFromLevel"] = cle->GetLFGFromLevel();
|
outclient["LFGFromLevel"] = cle->GetLFGFromLevel();
|
||||||
outclient["LFGToLevel"] = cle->GetLFGToLevel();
|
outclient["LFGToLevel"] = cle->GetLFGToLevel();
|
||||||
outclient["LFGMatchFilter"] = cle->GetLFGMatchFilter();
|
outclient["LFGMatchFilter"] = cle->GetLFGMatchFilter();
|
||||||
outclient["LFGComments"] = cle->GetLFGComments();
|
outclient["LFGComments"] = cle->GetLFGComments();
|
||||||
outclient["ClientVersion"] = cle->GetClientVersion();
|
outclient["ClientVersion"] = cle->GetClientVersion();
|
||||||
|
outclient["Trader"] = cle->GetTrader();
|
||||||
|
outclient["Buyer"] = cle->GetBuyer();
|
||||||
|
outclient["OfflineMode"] = cle->GetOfflineMode();
|
||||||
|
|
||||||
out["data"].append(outclient);
|
out["data"].append(outclient);
|
||||||
|
|
||||||
Iterator.Advance();
|
Iterator.Advance();
|
||||||
|
|||||||
@ -56,7 +56,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const
|
|||||||
const std::string& account_name = database.GetAccountName(ret.account_id);
|
const std::string& account_name = database.GetAccountName(ret.account_id);
|
||||||
|
|
||||||
ret.account_name = account_name;
|
ret.account_name = account_name;
|
||||||
ret.status = database.GetAccountStatus(ret.account_id);
|
ret.status = database.GetAccountStatus(ret.account_id).status;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,28 @@
|
|||||||
#include "../common/global_define.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "../common/version.h"
|
|
||||||
#include "../common/servertalk.h"
|
|
||||||
#include "../common/misc_functions.h"
|
|
||||||
#include "../common/eq_packet_structs.h"
|
|
||||||
#include "../common/packet_dump.h"
|
|
||||||
#include "../common/strings.h"
|
|
||||||
#include "../common/eqemu_logsys.h"
|
|
||||||
#include "login_server.h"
|
#include "login_server.h"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "../common/eq_packet_structs.h"
|
||||||
|
#include "../common/eqemu_logsys.h"
|
||||||
|
#include "../common/global_define.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/trader_repository.h"
|
||||||
|
#include "../common/servertalk.h"
|
||||||
|
#include "../common/strings.h"
|
||||||
|
#include "../common/version.h"
|
||||||
|
#include "cliententry.h"
|
||||||
|
#include "clientlist.h"
|
||||||
#include "login_server_list.h"
|
#include "login_server_list.h"
|
||||||
#include "zoneserver.h"
|
#include "world_config.h"
|
||||||
#include "worlddb.h"
|
#include "worlddb.h"
|
||||||
#include "zonelist.h"
|
#include "zonelist.h"
|
||||||
#include "clientlist.h"
|
#include "zoneserver.h"
|
||||||
#include "cliententry.h"
|
|
||||||
#include "world_config.h"
|
|
||||||
|
|
||||||
extern uint32 numzones;
|
extern uint32 numzones;
|
||||||
extern uint32 numplayers;
|
extern uint32 numplayers;
|
||||||
@ -44,9 +48,9 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id).status;
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
||||||
@ -124,14 +128,19 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
auto status_record = database.GetAccountStatus(id);
|
||||||
|
auto client = client_list.FindCLEByAccountID(id);
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
client->SetOfflineMode(status_record.offline);
|
||||||
|
}
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
||||||
id,
|
id,
|
||||||
status,
|
status_record.status,
|
||||||
utwr->lsaccountid,
|
utwr->lsaccountid,
|
||||||
utwr->worldid,
|
utwr->worldid,
|
||||||
utwr->FromID,
|
utwr->FromID,
|
||||||
@ -153,7 +162,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
utwrs->response = UserToWorldStatusSuccess;
|
utwrs->response = UserToWorldStatusSuccess;
|
||||||
|
|
||||||
if (Config->Locked == true) {
|
if (Config->Locked == true) {
|
||||||
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
|
if (status_record.status < (RuleI(GM, MinStatusToBypassLockedServer))) {
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Server locked and status is not high enough for account_id [{0}]",
|
"Server locked and status is not high enough for account_id [{0}]",
|
||||||
utwr->lsaccountid
|
utwr->lsaccountid
|
||||||
@ -165,27 +174,34 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32 x = Config->MaxClients;
|
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);
|
LogDebug("World at capacity account_id [{0}]", utwr->lsaccountid);
|
||||||
utwrs->response = UserToWorldStatusWorldAtCapacity;
|
utwrs->response = UserToWorldStatusWorldAtCapacity;
|
||||||
SendPacket(&outpack);
|
SendPacket(&outpack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == -1) {
|
if (status_record.status == -1) {
|
||||||
LogDebug("User suspended account_id [{0}]", utwr->lsaccountid);
|
LogDebug("User suspended account_id [{0}]", utwr->lsaccountid);
|
||||||
utwrs->response = UserToWorldStatusSuspended;
|
utwrs->response = UserToWorldStatusSuspended;
|
||||||
SendPacket(&outpack);
|
SendPacket(&outpack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == -2) {
|
if (status_record.status == -2) {
|
||||||
LogDebug("User banned account_id [{0}]", utwr->lsaccountid);
|
LogDebug("User banned account_id [{0}]", utwr->lsaccountid);
|
||||||
utwrs->response = UserToWorldStatusBanned;
|
utwrs->response = UserToWorldStatusBanned;
|
||||||
SendPacket(&outpack);
|
SendPacket(&outpack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status_record.offline) {
|
||||||
|
LogDebug("User has an offline character for account_id [{0}]", utwr->lsaccountid);
|
||||||
|
utwrs->response = UserToWorldStatusOffilineTraderBuyer;
|
||||||
|
SendPacket(&outpack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (RuleB(World, EnforceCharacterLimitAtLogin)) {
|
if (RuleB(World, EnforceCharacterLimitAtLogin)) {
|
||||||
if (ClientList::Instance()->IsAccountInGame(utwr->lsaccountid)) {
|
if (ClientList::Instance()->IsAccountInGame(utwr->lsaccountid)) {
|
||||||
LogDebug("User already online account_id [{0}]", utwr->lsaccountid);
|
LogDebug("User already online account_id [{0}]", utwr->lsaccountid);
|
||||||
@ -573,6 +589,14 @@ bool LoginServer::Connect()
|
|||||||
std::placeholders::_2
|
std::placeholders::_2
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
m_client->OnMessage(
|
||||||
|
ServerOP_UsertoWorldCancelOfflineRequest,
|
||||||
|
std::bind(
|
||||||
|
&LoginServer::ProcessUserToWorldCancelOfflineRequest,
|
||||||
|
this,
|
||||||
|
std::placeholders::_1,
|
||||||
|
std::placeholders::_2)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -688,3 +712,87 @@ 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 trader = TraderRepository::GetAccountZoneIdAndInstanceIdByAccountId(database, id);
|
||||||
|
if (trader.id &&
|
||||||
|
zoneserver_list.IsZoneBootedByZoneIdAndInstanceId(trader.char_zone_id, trader.char_zone_instance_id)) {
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 5a(1) - World Checked offline users zone/instance is booted. "
|
||||||
|
"Sending packet to zone id {} instance id {}",
|
||||||
|
trader.char_zone_id,
|
||||||
|
trader.char_zone_instance_id);
|
||||||
|
|
||||||
|
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineRequest;
|
||||||
|
zoneserver_list.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.");
|
||||||
|
AccountRepository::SetOfflineStatus(database, id, false);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 5b(3) - World clearing trader and buyer tablese.");
|
||||||
|
TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}'", trader.id));
|
||||||
|
BuyerRepository::DeleteBuyer(database, trader.id);
|
||||||
|
|
||||||
|
server_packet.opcode = ServerOP_UsertoWorldCancelOfflineResponse;
|
||||||
|
SendPacket(&server_packet);
|
||||||
|
}
|
||||||
|
|||||||
@ -49,6 +49,7 @@ private:
|
|||||||
void ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p);
|
void ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p);
|
||||||
void ProcessLSRemoteAddr(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 ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p);
|
||||||
|
void ProcessUserToWorldCancelOfflineRequest(uint16_t opcode, EQ::Net::Packet &p);
|
||||||
|
|
||||||
std::unique_ptr<EQ::Timer> m_keepalive;
|
std::unique_ptr<EQ::Timer> m_keepalive;
|
||||||
|
|
||||||
|
|||||||
@ -1018,3 +1018,15 @@ void ZSList::QueueServerReload(ServerReload::Type &type)
|
|||||||
m_queued_reloads.emplace_back(type);
|
m_queued_reloads.emplace_back(type);
|
||||||
m_queued_reloads_mutex.unlock();
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ public:
|
|||||||
bool SendPacketToZonesWithGMs(ServerPacket *pack);
|
bool SendPacketToZonesWithGMs(ServerPacket *pack);
|
||||||
bool SendPacketToBootedZones(ServerPacket* pack);
|
bool SendPacketToBootedZones(ServerPacket* pack);
|
||||||
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
||||||
|
bool IsZoneBootedByZoneIdAndInstanceId(uint32 zone_id, uint32 instance_id) const;
|
||||||
|
|
||||||
EQTime worldclock;
|
EQTime worldclock;
|
||||||
|
|
||||||
|
|||||||
@ -1726,9 +1726,34 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default: {
|
||||||
return;
|
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.SendPacket(&server_packet);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||||
|
|||||||
@ -59,7 +59,7 @@ EQ::Net::WebsocketLoginStatus CheckLogin(
|
|||||||
|
|
||||||
ret.account_name = database.GetAccountName(static_cast<uint32>(ret.account_id));
|
ret.account_name = database.GetAccountName(static_cast<uint32>(ret.account_id));
|
||||||
ret.logged_in = true;
|
ret.logged_in = true;
|
||||||
ret.status = database.GetAccountStatus(ret.account_id);
|
ret.status = database.GetAccountStatus(ret.account_id).status;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -510,10 +510,10 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
client_data_loaded = false;
|
client_data_loaded = false;
|
||||||
berserk = false;
|
berserk = false;
|
||||||
dead = false;
|
dead = false;
|
||||||
eqs = ieqs;
|
eqs = ieqs ? ieqs : nullptr;
|
||||||
ip = eqs->GetRemoteIP();
|
ip = eqs ? eqs->GetRemoteIP() : 0;
|
||||||
port = ntohs(eqs->GetRemotePort());
|
port = eqs ? ntohs(eqs->GetRemotePort()) : 0;
|
||||||
client_state = CLIENT_CONNECTING;
|
client_state = eqs ? CLIENT_CONNECTING : CLIENT_CONNECTED;
|
||||||
SetTrader(false);
|
SetTrader(false);
|
||||||
Haste = 0;
|
Haste = 0;
|
||||||
SetCustomerID(0);
|
SetCustomerID(0);
|
||||||
@ -700,6 +700,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
m_parcels.clear();
|
m_parcels.clear();
|
||||||
|
|
||||||
m_buyer_id = 0;
|
m_buyer_id = 0;
|
||||||
|
m_offline = false;
|
||||||
|
|
||||||
SetBotPulling(false);
|
SetBotPulling(false);
|
||||||
SetBotPrecombat(false);
|
SetBotPrecombat(false);
|
||||||
@ -728,10 +729,12 @@ Client::~Client() {
|
|||||||
zone->ClearEXPModifier(this);
|
zone->ClearEXPModifier(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsZoning()) {
|
if (!IsZoning() && IsInAGuild()) {
|
||||||
if(IsInAGuild()) {
|
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||||
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
if (IsOffline()) {
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), GetZoneID(), time(nullptr), 1);
|
||||||
|
} else {
|
||||||
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,11 +746,11 @@ Client::~Client() {
|
|||||||
if (merc)
|
if (merc)
|
||||||
merc->Depop();
|
merc->Depop();
|
||||||
|
|
||||||
if(IsTrader()) {
|
if(IsTrader() && !IsOffline()) {
|
||||||
TraderEndTrader();
|
TraderEndTrader();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsBuyer()) {
|
if(IsBuyer() && !IsOffline()) {
|
||||||
ToggleBuyerMode(false);
|
ToggleBuyerMode(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,7 +782,9 @@ Client::~Client() {
|
|||||||
if(isgrouped && !bZoning && is_zone_loaded)
|
if(isgrouped && !bZoning && is_zone_loaded)
|
||||||
LeaveGroup();
|
LeaveGroup();
|
||||||
|
|
||||||
UpdateWho(2);
|
if (!IsOffline() && !IsTrader()) {
|
||||||
|
UpdateWho(2);
|
||||||
|
}
|
||||||
|
|
||||||
if(IsHoveringForRespawn())
|
if(IsHoveringForRespawn())
|
||||||
{
|
{
|
||||||
@ -2155,6 +2160,9 @@ void Client::UpdateWho(uint8 remove)
|
|||||||
s->race = GetRace();
|
s->race = GetRace();
|
||||||
s->class_ = GetClass();
|
s->class_ = GetClass();
|
||||||
s->level = GetLevel();
|
s->level = GetLevel();
|
||||||
|
s->trader = IsTrader();
|
||||||
|
s->buyer = IsBuyer();
|
||||||
|
s->offline = IsOffline();
|
||||||
|
|
||||||
if (m_pp.anon == 0) {
|
if (m_pp.anon == 0) {
|
||||||
s->anon = 0;
|
s->anon = 0;
|
||||||
@ -2223,7 +2231,7 @@ void Client::FriendsWho(char *FriendsString) {
|
|||||||
void Client::UpdateAdmin(bool from_database) {
|
void Client::UpdateAdmin(bool from_database) {
|
||||||
int16 tmp = admin;
|
int16 tmp = admin;
|
||||||
if (from_database) {
|
if (from_database) {
|
||||||
admin = database.GetAccountStatus(account_id);
|
admin = database.GetAccountStatus(account_id).status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp == admin && from_database) {
|
if (tmp == admin && from_database) {
|
||||||
@ -2539,6 +2547,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
|||||||
ns->spawn.guildID = GuildID();
|
ns->spawn.guildID = GuildID();
|
||||||
ns->spawn.trader = IsTrader();
|
ns->spawn.trader = IsTrader();
|
||||||
ns->spawn.buyer = IsBuyer();
|
ns->spawn.buyer = IsBuyer();
|
||||||
|
ns->spawn.offline = IsOffline();
|
||||||
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
||||||
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
||||||
ns->spawn.show_name = true;
|
ns->spawn.show_name = true;
|
||||||
|
|||||||
@ -416,6 +416,8 @@ public:
|
|||||||
int32 FindNextFreeParcelSlot(uint32 char_id);
|
int32 FindNextFreeParcelSlot(uint32 char_id);
|
||||||
int32 FindNextFreeParcelSlotUsingMemory();
|
int32 FindNextFreeParcelSlotUsingMemory();
|
||||||
void SendParcelIconStatus();
|
void SendParcelIconStatus();
|
||||||
|
bool IsOffline() { return m_offline; }
|
||||||
|
void SetOffline(bool status) { m_offline = status; }
|
||||||
|
|
||||||
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
||||||
void SendBecomeTrader(BazaarTraderBarterActions action, uint32 trader_id);
|
void SendBecomeTrader(BazaarTraderBarterActions action, uint32 trader_id);
|
||||||
@ -508,7 +510,12 @@ public:
|
|||||||
inline bool ClientDataLoaded() const { return client_data_loaded; }
|
inline bool ClientDataLoaded() const { return client_data_loaded; }
|
||||||
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
|
inline bool Connected() const { return (client_state == CLIENT_CONNECTED); }
|
||||||
inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); }
|
inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); }
|
||||||
inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; }
|
inline void Disconnect() {
|
||||||
|
if (eqs) {
|
||||||
|
eqs->Close();
|
||||||
|
client_state = DISCONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); }
|
inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); }
|
||||||
void Kick(const std::string &reason);
|
void Kick(const std::string &reason);
|
||||||
void WorldKick();
|
void WorldKick();
|
||||||
@ -2117,6 +2124,7 @@ private:
|
|||||||
uint32 m_trader_count{};
|
uint32 m_trader_count{};
|
||||||
std::map<int16, std::tuple<uint32, int32, std::string>> m_trader_merchant_list{}; // itemid, qty, item_unique_id
|
std::map<int16, std::tuple<uint32, int32, std::string>> m_trader_merchant_list{}; // itemid, qty, item_unique_id
|
||||||
uint32 m_buyer_id;
|
uint32 m_buyer_id;
|
||||||
|
bool m_offline;
|
||||||
uint32 m_barter_time;
|
uint32 m_barter_time;
|
||||||
int32 m_parcel_platinum;
|
int32 m_parcel_platinum;
|
||||||
int32 m_parcel_gold;
|
int32 m_parcel_gold;
|
||||||
@ -2446,6 +2454,68 @@ public:
|
|||||||
bool IsFilteredAFKPacket(const EQApplicationPacket *p);
|
bool IsFilteredAFKPacket(const EQApplicationPacket *p);
|
||||||
void CheckAutoIdleAFK(PlayerPositionUpdateClient_Struct *p);
|
void CheckAutoIdleAFK(PlayerPositionUpdateClient_Struct *p);
|
||||||
void SyncWorldPositionsToClient(bool ignore_idle = false);
|
void SyncWorldPositionsToClient(bool ignore_idle = false);
|
||||||
|
|
||||||
|
|
||||||
|
Mob* GetMob() {
|
||||||
|
return Mob::GetMob();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clone(Client& in)
|
||||||
|
{
|
||||||
|
WID = in.WID;
|
||||||
|
admin = in.admin;
|
||||||
|
guild_id = in.guild_id;
|
||||||
|
guildrank = in.guildrank;
|
||||||
|
LFG = in.LFG;
|
||||||
|
AFK = in.AFK;
|
||||||
|
trader_id = in.trader_id;
|
||||||
|
m_buyer_id = in.m_buyer_id;
|
||||||
|
race = in.race;
|
||||||
|
class_ = in.class_;
|
||||||
|
size = in.size;
|
||||||
|
deity = in.deity;
|
||||||
|
texture = in.texture;
|
||||||
|
m_ClientVersion = in.m_ClientVersion;
|
||||||
|
m_ClientVersionBit = in.m_ClientVersionBit;
|
||||||
|
character_id = in.character_id;
|
||||||
|
account_id = in.account_id;
|
||||||
|
lsaccountid = in.lsaccountid;
|
||||||
|
|
||||||
|
m_pp.platinum = in.m_pp.platinum;
|
||||||
|
m_pp.gold = in.m_pp.gold;
|
||||||
|
m_pp.silver = in.m_pp.silver;
|
||||||
|
m_pp.copper = in.m_pp.copper;
|
||||||
|
m_pp.platinum_bank = in.m_pp.platinum_bank;
|
||||||
|
m_pp.gold_bank = in.m_pp.gold_bank;
|
||||||
|
m_pp.silver_bank = in.m_pp.silver_bank;
|
||||||
|
m_pp.copper_bank = in.m_pp.copper_bank;
|
||||||
|
m_pp.platinum_cursor = in.m_pp.platinum_cursor;
|
||||||
|
m_pp.gold_cursor = in.m_pp.gold_cursor;
|
||||||
|
m_pp.silver_cursor = in.m_pp.silver_cursor;
|
||||||
|
m_pp.copper_cursor = in.m_pp.copper_cursor;
|
||||||
|
m_pp.currentRadCrystals = in.m_pp.currentRadCrystals;
|
||||||
|
m_pp.careerRadCrystals = in.m_pp.careerRadCrystals;
|
||||||
|
m_pp.currentEbonCrystals = in.m_pp.currentEbonCrystals;
|
||||||
|
m_pp.careerEbonCrystals = in.m_pp.careerEbonCrystals;
|
||||||
|
m_pp.gm = in.m_pp.gm;
|
||||||
|
|
||||||
|
m_inv.SetInventoryVersion(in.m_ClientVersion);
|
||||||
|
SetBodyType(in.GetBodyType(), false);
|
||||||
|
|
||||||
|
for (auto [slot, item] : in.m_inv.GetPersonal()) {
|
||||||
|
if (item) {
|
||||||
|
m_inv.GetPersonal()[slot] = item->Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto [slot, item] : in.m_inv.GetWorn()) {
|
||||||
|
if (item) {
|
||||||
|
m_inv.GetWorn()[slot] = item->Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloneMob(*in.GetMob());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/eqemu_logsys.h"
|
#include "../common/eqemu_logsys.h"
|
||||||
#include "../common/opcodemgr.h"
|
#include "../common/opcodemgr.h"
|
||||||
#include "../common/raid.h"
|
#include "../common/raid.h"
|
||||||
|
#include "../common/repositories/character_offline_transactions_repository.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -323,6 +324,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin;
|
ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin;
|
||||||
ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem;
|
ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem;
|
||||||
ConnectedOpcodes[OP_MoveMultipleItems] = &Client::Handle_OP_MoveMultipleItems;
|
ConnectedOpcodes[OP_MoveMultipleItems] = &Client::Handle_OP_MoveMultipleItems;
|
||||||
|
ConnectedOpcodes[OP_Offline] = &Client::Handle_OP_Offline;
|
||||||
ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer;
|
ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer;
|
||||||
ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster;
|
ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster;
|
||||||
ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory;
|
ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory;
|
||||||
@ -857,6 +859,54 @@ void Client::CompleteConnect()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto offline_transactions_trader = CharacterOfflineTransactionsRepository::GetWhere(
|
||||||
|
database, fmt::format("`character_id` = '{}' AND `type` = '{}'", CharacterID(), TRADER_TRANSACTION)
|
||||||
|
);
|
||||||
|
if (offline_transactions_trader.size() > 0) {
|
||||||
|
Message(Chat::Yellow, "You sold the following items while in offline trader mode:");
|
||||||
|
|
||||||
|
for (auto const &t: offline_transactions_trader) {
|
||||||
|
Message(
|
||||||
|
Chat::Yellow,
|
||||||
|
fmt::format(
|
||||||
|
"You sold {} {}{} to {} for {}.",
|
||||||
|
t.quantity,
|
||||||
|
t.item_name,
|
||||||
|
t.quantity > 1 ? "s" : "",
|
||||||
|
t.buyer_name,
|
||||||
|
DetermineMoneyString(t.price))
|
||||||
|
.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::DeleteWhere(
|
||||||
|
database, fmt::format("`character_id` = '{}' AND `type` = '{}'", CharacterID(), TRADER_TRANSACTION)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto offline_transactions_buyer = CharacterOfflineTransactionsRepository::GetWhere(
|
||||||
|
database, fmt::format("`character_id` = '{}' AND `type` = '{}'", CharacterID(), BUYER_TRANSACTION)
|
||||||
|
);
|
||||||
|
if (offline_transactions_buyer.size() > 0) {
|
||||||
|
Message(Chat::Yellow, "You bought the following items while in offline buyer mode:");
|
||||||
|
|
||||||
|
for (auto const &t: offline_transactions_buyer) {
|
||||||
|
Message(
|
||||||
|
Chat::Yellow,
|
||||||
|
fmt::format(
|
||||||
|
"You bought {} {}{} from {} for {}.",
|
||||||
|
t.quantity,
|
||||||
|
t.item_name,
|
||||||
|
t.quantity > 1 ? "s" : "",
|
||||||
|
t.buyer_name,
|
||||||
|
DetermineMoneyString(t.price))
|
||||||
|
.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::DeleteWhere(
|
||||||
|
database, fmt::format("`character_id` = '{}' AND `type` = '{}'", CharacterID(), BUYER_TRANSACTION)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
||||||
SendParcelStatus();
|
SendParcelStatus();
|
||||||
}
|
}
|
||||||
@ -900,7 +950,7 @@ void Client::CompleteConnect()
|
|||||||
SendGuildMembersList();
|
SendGuildMembersList();
|
||||||
}
|
}
|
||||||
|
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr), 0);
|
||||||
|
|
||||||
SendGuildList();
|
SendGuildList();
|
||||||
if (GetGuildListDirty()) {
|
if (GetGuildListDirty()) {
|
||||||
@ -17360,3 +17410,51 @@ void Client::SyncWorldPositionsToClient(bool ignore_idle)
|
|||||||
m_is_idle = false;
|
m_is_idle = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Client::Handle_OP_Offline(const EQApplicationPacket *app)
|
||||||
|
{
|
||||||
|
if (IsThereACustomer()) {
|
||||||
|
auto customer = entity_list.GetClientByID(GetCustomerID());
|
||||||
|
if (customer) {
|
||||||
|
auto end_session = new EQApplicationPacket(OP_ShopEnd);
|
||||||
|
customer->FastQueuePacket(&end_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountRepository::SetOfflineStatus(database, AccountID(), true);
|
||||||
|
SetOffline(true);
|
||||||
|
|
||||||
|
EQStreamInterface *eqsi = nullptr;
|
||||||
|
auto offline_client = new Client(eqsi);
|
||||||
|
|
||||||
|
database.LoadCharacterData(CharacterID(), &offline_client->GetPP(), &offline_client->GetEPP());
|
||||||
|
offline_client->Clone(*this);
|
||||||
|
offline_client->GetInv().SetGMInventory(true);
|
||||||
|
offline_client->SetPosition(GetX(), GetY(), GetZ());
|
||||||
|
offline_client->SetHeading(GetHeading());
|
||||||
|
offline_client->SetSpawned();
|
||||||
|
offline_client->SetBecomeNPC(false);
|
||||||
|
offline_client->SetOffline(true);
|
||||||
|
entity_list.AddClient(offline_client);
|
||||||
|
|
||||||
|
if (IsBuyer()) {
|
||||||
|
offline_client->SetBuyerID(offline_client->CharacterID());
|
||||||
|
if (!BuyerRepository::UpdateBuyerEntityID(database, CharacterID(), GetID(), offline_client->GetID())) {
|
||||||
|
entity_list.RemoveMob(offline_client->CastToMob()->GetID());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offline_client->SetTrader(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnDisconnect(true);
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket();
|
||||||
|
offline_client->CreateSpawnPacket(outapp);
|
||||||
|
entity_list.QueueClients(nullptr, outapp, false);
|
||||||
|
safe_delete(outapp);
|
||||||
|
|
||||||
|
offline_client->UpdateWho(3);
|
||||||
|
}
|
||||||
|
|||||||
@ -226,6 +226,7 @@
|
|||||||
void Handle_OP_MoveCoin(const EQApplicationPacket *app);
|
void Handle_OP_MoveCoin(const EQApplicationPacket *app);
|
||||||
void Handle_OP_MoveItem(const EQApplicationPacket *app);
|
void Handle_OP_MoveItem(const EQApplicationPacket *app);
|
||||||
void Handle_OP_MoveMultipleItems(const EQApplicationPacket *app);
|
void Handle_OP_MoveMultipleItems(const EQApplicationPacket *app);
|
||||||
|
void Handle_OP_Offline(const EQApplicationPacket *app);
|
||||||
void Handle_OP_OpenContainer(const EQApplicationPacket *app);
|
void Handle_OP_OpenContainer(const EQApplicationPacket *app);
|
||||||
void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app);
|
void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app);
|
||||||
void Handle_OP_OpenInventory(const EQApplicationPacket *app);
|
void Handle_OP_OpenInventory(const EQApplicationPacket *app);
|
||||||
|
|||||||
@ -178,7 +178,7 @@ bool Client::Process() {
|
|||||||
}
|
}
|
||||||
if (IsInAGuild()) {
|
if (IsInAGuild()) {
|
||||||
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
|
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
|
||||||
@ -207,7 +207,7 @@ bool Client::Process() {
|
|||||||
Save();
|
Save();
|
||||||
if (IsInAGuild()) {
|
if (IsInAGuild()) {
|
||||||
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetMerc())
|
if (GetMerc())
|
||||||
@ -588,7 +588,7 @@ bool Client::Process() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_state != CLIENT_LINKDEAD && !eqs->CheckState(ESTABLISHED)) {
|
if (eqs && client_state != CLIENT_LINKDEAD && !eqs->CheckState(ESTABLISHED)) {
|
||||||
OnDisconnect(true);
|
OnDisconnect(true);
|
||||||
LogInfo("Client linkdead: {}", name);
|
LogInfo("Client linkdead: {}", name);
|
||||||
|
|
||||||
@ -599,7 +599,7 @@ bool Client::Process() {
|
|||||||
}
|
}
|
||||||
if (IsInAGuild()) {
|
if (IsInAGuild()) {
|
||||||
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -617,7 +617,7 @@ bool Client::Process() {
|
|||||||
|
|
||||||
/************ Get all packets from packet manager out queue and process them ************/
|
/************ Get all packets from packet manager out queue and process them ************/
|
||||||
EQApplicationPacket *app = nullptr;
|
EQApplicationPacket *app = nullptr;
|
||||||
if (!eqs->CheckState(CLOSING))
|
if (eqs && !eqs->CheckState(CLOSING))
|
||||||
{
|
{
|
||||||
while (app = eqs->PopPacket()) {
|
while (app = eqs->PopPacket()) {
|
||||||
HandlePacket(app);
|
HandlePacket(app);
|
||||||
@ -627,7 +627,7 @@ bool Client::Process() {
|
|||||||
|
|
||||||
ClientToNpcAggroProcess();
|
ClientToNpcAggroProcess();
|
||||||
|
|
||||||
if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED)))
|
if (eqs && client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED)))
|
||||||
{
|
{
|
||||||
//client logged out or errored out
|
//client logged out or errored out
|
||||||
//ResetTrade();
|
//ResetTrade();
|
||||||
|
|||||||
@ -5006,15 +5006,31 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
|
|||||||
strcpy(WAPP1->Name, ClientEntry->GetName());
|
strcpy(WAPP1->Name, ClientEntry->GetName());
|
||||||
Buffer += sizeof(WhoAllPlayerPart1) + strlen(WAPP1->Name);
|
Buffer += sizeof(WhoAllPlayerPart1) + strlen(WAPP1->Name);
|
||||||
WhoAllPlayerPart2* WAPP2 = (WhoAllPlayerPart2*)Buffer;
|
WhoAllPlayerPart2* WAPP2 = (WhoAllPlayerPart2*)Buffer;
|
||||||
|
WAPP2->RankMSGID = 0xFFFFFFFF;
|
||||||
|
|
||||||
if (ClientEntry->IsTrader())
|
if (ClientEntry->IsOffline()) {
|
||||||
WAPP2->RankMSGID = 12315;
|
if (ClientEntry->IsTrader()) {
|
||||||
else if (ClientEntry->IsBuyer())
|
WAPP1->PIDMSGID = 0x0430;
|
||||||
WAPP2->RankMSGID = 6056;
|
}
|
||||||
else if (ClientEntry->Admin() >= AccountStatus::Steward && ClientEntry->GetGM())
|
if (ClientEntry->IsBuyer()) {
|
||||||
|
WAPP1->PIDMSGID = 0x0420;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (ClientEntry->IsTrader()) {
|
||||||
|
WAPP2->RankMSGID = 12315;
|
||||||
|
}
|
||||||
|
else if (ClientEntry->IsBuyer()) {
|
||||||
|
WAPP2->RankMSGID = 6056;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClientEntry->Admin() >= AccountStatus::Steward && ClientEntry->GetGM()) {
|
||||||
WAPP2->RankMSGID = 12312;
|
WAPP2->RankMSGID = 12312;
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
WAPP2->RankMSGID = 0xFFFFFFFF;
|
WAPP2->RankMSGID = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(WAPP2->Guild, GuildName.c_str());
|
strcpy(WAPP2->Guild, GuildName.c_str());
|
||||||
Buffer += sizeof(WhoAllPlayerPart2) + strlen(WAPP2->Guild);
|
Buffer += sizeof(WhoAllPlayerPart2) + strlen(WAPP2->Guild);
|
||||||
|
|||||||
@ -426,10 +426,11 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack)
|
|||||||
auto outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct));
|
auto outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct));
|
||||||
auto gmus = (GuildMemberUpdate_Struct *) outapp->pBuffer;
|
auto gmus = (GuildMemberUpdate_Struct *) outapp->pBuffer;
|
||||||
|
|
||||||
gmus->GuildID = sgmus->guild_id;
|
gmus->GuildID = sgmus->guild_id;
|
||||||
gmus->ZoneID = sgmus->zone_id;
|
gmus->ZoneID = sgmus->zone_id;
|
||||||
gmus->InstanceID = 0;
|
gmus->InstanceID = 0;
|
||||||
gmus->LastSeen = sgmus->last_seen;
|
gmus->LastSeen = sgmus->last_seen;
|
||||||
|
gmus->offline_mode = sgmus->offline_mode;
|
||||||
strn0cpy(gmus->MemberName, sgmus->member_name, sizeof(gmus->MemberName));
|
strn0cpy(gmus->MemberName, sgmus->member_name, sizeof(gmus->MemberName));
|
||||||
|
|
||||||
entity_list.QueueClientsGuild(outapp, sgmus->guild_id);
|
entity_list.QueueClientsGuild(outapp, sgmus->guild_id);
|
||||||
@ -652,17 +653,24 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneGuildManager::SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen)
|
void ZoneGuildManager::SendGuildMemberUpdateToWorld(
|
||||||
|
const char *MemberName,
|
||||||
|
uint32 GuildID,
|
||||||
|
uint16 ZoneID,
|
||||||
|
uint32 LastSeen,
|
||||||
|
uint32 offline_mode
|
||||||
|
)
|
||||||
{
|
{
|
||||||
auto pack = new ServerPacket(ServerOP_GuildMemberUpdate, sizeof(ServerGuildMemberUpdate_Struct));
|
auto pack = new ServerPacket(ServerOP_GuildMemberUpdate, sizeof(ServerGuildMemberUpdate_Struct));
|
||||||
|
|
||||||
ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer;
|
auto sgmus = (ServerGuildMemberUpdate_Struct *) pack->pBuffer;
|
||||||
sgmus->guild_id = GuildID;
|
sgmus->guild_id = GuildID;
|
||||||
|
sgmus->zone_id = ZoneID;
|
||||||
|
sgmus->last_seen = LastSeen;
|
||||||
|
sgmus->offline_mode = offline_mode;
|
||||||
strn0cpy(sgmus->member_name, MemberName, sizeof(sgmus->member_name));
|
strn0cpy(sgmus->member_name, MemberName, sizeof(sgmus->member_name));
|
||||||
sgmus->zone_id = ZoneID;
|
|
||||||
sgmus->last_seen = LastSeen;
|
|
||||||
worldserver.SendPacket(pack);
|
|
||||||
|
|
||||||
|
worldserver.SendPacket(pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1517,14 +1525,12 @@ uint8* ZoneGuildManager::MakeGuildMembers(uint32 guild_id, const char* prefix_na
|
|||||||
PutField(total_tribute);
|
PutField(total_tribute);
|
||||||
PutField(last_tribute);
|
PutField(last_tribute);
|
||||||
SlideStructString(note_buf, ci->public_note);
|
SlideStructString(note_buf, ci->public_note);
|
||||||
//e->zoneinstance = 0;
|
e->zone_id = 0; //If zone_id is 0 and we rely on the current world routine, the notes/tribute tabs are not updated for online characters.
|
||||||
if (ci->online) {
|
e->offline_mode = 0;
|
||||||
e->zone_id = ci->zone_id; //This routine, if there is a zone_id, will update the entire guild window (roster, notes, tribute) for online characters.
|
if (ci->online || ci->offline_mode) {
|
||||||
|
e->zone_id = ci->zone_id; //This routine, if there is a zone_id, will update the entire guild window (roster, notes, tribute) for online characters.
|
||||||
|
e->offline_mode = ci->offline_mode;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
e->zone_id = 0; //If zone_id is 0 and we rely on the current world routine, the notes/tribute tabs are not updated for online characters.
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef SlideStructString
|
#undef SlideStructString
|
||||||
#undef PutFieldN
|
#undef PutFieldN
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ public:
|
|||||||
|
|
||||||
void RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank);
|
void RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank);
|
||||||
bool VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank);
|
bool VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank);
|
||||||
void SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen);
|
void SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen, uint32 offline_mode);
|
||||||
void RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID);
|
void RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID);
|
||||||
void UpdateRankPermission(uint32 gid, uint32 charid, uint32 fid, uint32 rank, uint32 value);
|
void UpdateRankPermission(uint32 gid, uint32 charid, uint32 fid, uint32 rank, uint32 value);
|
||||||
void SendPermissionUpdate(uint32 guild_id, uint32 rank, uint32 function_id, uint32 value);
|
void SendPermissionUpdate(uint32 guild_id, uint32 rank, uint32 function_id, uint32 value);
|
||||||
|
|||||||
99
zone/mob.h
99
zone/mob.h
@ -1952,6 +1952,105 @@ private:
|
|||||||
|
|
||||||
void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana);
|
void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana);
|
||||||
void HandleDoorOpen();
|
void HandleDoorOpen();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Mob* GetMob() { return this; }
|
||||||
|
|
||||||
|
void CloneMob(Mob& in) {
|
||||||
|
strn0cpy(name, in.name, 64);
|
||||||
|
strn0cpy(orig_name, in.orig_name, 64);
|
||||||
|
strn0cpy(lastname, in.lastname, 64);
|
||||||
|
current_hp = in.current_hp;
|
||||||
|
max_hp = in.max_hp;
|
||||||
|
base_hp = in.base_hp;
|
||||||
|
gender = in.gender;
|
||||||
|
race = in.race;
|
||||||
|
base_gender = in.base_gender;
|
||||||
|
base_race = in.race;
|
||||||
|
use_model = in.use_model;
|
||||||
|
class_ = in.class_;
|
||||||
|
bodytype = in.bodytype;
|
||||||
|
orig_bodytype = in.orig_bodytype;
|
||||||
|
deity = in.deity;
|
||||||
|
level = in.level;
|
||||||
|
orig_level = in.orig_level;
|
||||||
|
npctype_id = in.npctype_id;
|
||||||
|
size = in.size;
|
||||||
|
base_size = in.base_size;
|
||||||
|
runspeed = in.runspeed;
|
||||||
|
texture = in.texture;
|
||||||
|
helmtexture = in.helmtexture;
|
||||||
|
armtexture = in.armtexture;
|
||||||
|
bracertexture = in.bracertexture;
|
||||||
|
handtexture = in.handtexture;
|
||||||
|
legtexture = in.legtexture;
|
||||||
|
feettexture = in.feettexture;
|
||||||
|
multitexture = in.multitexture;
|
||||||
|
haircolor = in.haircolor;
|
||||||
|
beardcolor = in.beardcolor;
|
||||||
|
eyecolor1 = in.eyecolor1;
|
||||||
|
eyecolor2 = in.eyecolor2;
|
||||||
|
hairstyle = in.hairstyle;
|
||||||
|
luclinface = in.luclinface;
|
||||||
|
beard = in.beard;
|
||||||
|
drakkin_heritage = in.drakkin_heritage;
|
||||||
|
drakkin_tattoo = in.drakkin_tattoo;
|
||||||
|
drakkin_details = in.drakkin_details;
|
||||||
|
attack_speed = in.attack_speed;
|
||||||
|
attack_delay = in.attack_delay;
|
||||||
|
slow_mitigation = in.slow_mitigation;
|
||||||
|
findable = in.findable;
|
||||||
|
trackable = in.trackable;
|
||||||
|
has_shield_equipped = in.has_shield_equipped;
|
||||||
|
has_two_hand_blunt_equipped = in.has_two_hand_blunt_equipped;
|
||||||
|
has_two_hander_equipped = in.has_two_hander_equipped;
|
||||||
|
has_dual_weapons_equipped = in.has_dual_weapons_equipped;
|
||||||
|
can_facestab = in.can_facestab;
|
||||||
|
has_numhits = in.has_numhits;
|
||||||
|
has_MGB = in.has_MGB;
|
||||||
|
has_ProjectIllusion = in.has_ProjectIllusion;
|
||||||
|
SpellPowerDistanceMod = in.SpellPowerDistanceMod;
|
||||||
|
last_los_check = in.last_los_check;
|
||||||
|
aa_title = in.aa_title;
|
||||||
|
AC = in.AC;
|
||||||
|
ATK = in.ATK;
|
||||||
|
STR = in.STR;
|
||||||
|
STA = in.STA;
|
||||||
|
DEX = in.DEX;
|
||||||
|
AGI = in.AGI;
|
||||||
|
INT = in.INT;
|
||||||
|
WIS = in.WIS;
|
||||||
|
CHA = in.CHA;
|
||||||
|
MR = in.MR;
|
||||||
|
extra_haste = in.extra_haste;
|
||||||
|
bEnraged = in.bEnraged;
|
||||||
|
current_mana = in.current_mana;
|
||||||
|
max_mana = in.max_mana;
|
||||||
|
hp_regen = in.hp_regen;
|
||||||
|
hp_regen_per_second = in.hp_regen_per_second;
|
||||||
|
mana_regen = in.mana_regen;
|
||||||
|
ooc_regen = in.ooc_regen;
|
||||||
|
maxlevel = in.maxlevel;
|
||||||
|
scalerate = in.scalerate;
|
||||||
|
invisible = in.invisible;
|
||||||
|
invisible_undead = in.invisible_undead;
|
||||||
|
invisible_animals = in.invisible_animals;
|
||||||
|
sneaking = in.sneaking;
|
||||||
|
hidden = in.hidden;
|
||||||
|
improved_hidden = in.improved_hidden;
|
||||||
|
invulnerable = in.invulnerable;
|
||||||
|
qglobal = in.qglobal;
|
||||||
|
spawned = in.spawned;
|
||||||
|
rare_spawn = in.rare_spawn;
|
||||||
|
always_aggro = in.always_aggro;
|
||||||
|
heroic_strikethrough = in.heroic_strikethrough;
|
||||||
|
keeps_sold_items = in.keeps_sold_items;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_APPEARANCE_EFFECTS; i++) {
|
||||||
|
appearance_effects_id[i] = in.appearance_effects_id[i];
|
||||||
|
appearance_effects_slot[i] = in.appearance_effects_slot[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -26,6 +26,8 @@
|
|||||||
#include "../common/repositories/trader_repository.h"
|
#include "../common/repositories/trader_repository.h"
|
||||||
#include "../common/repositories/buyer_repository.h"
|
#include "../common/repositories/buyer_repository.h"
|
||||||
#include "../common/repositories/buyer_buy_lines_repository.h"
|
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||||
|
#include "../common/repositories/character_offline_transactions_repository.h"
|
||||||
|
#include "../common/repositories/account_repository.h"
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
@ -907,6 +909,7 @@ void Client::TraderStartTrader(const EQApplicationPacket *app)
|
|||||||
SetTrader(true);
|
SetTrader(true);
|
||||||
SendTraderMode(TraderOn);
|
SendTraderMode(TraderOn);
|
||||||
SendBecomeTraderToWorld(this, TraderOn);
|
SendBecomeTraderToWorld(this, TraderOn);
|
||||||
|
UpdateWho();
|
||||||
LogTrading("Trader Mode ON for Player [{}] with client version {}.", GetCleanName(), (uint32) ClientVersion());
|
LogTrading("Trader Mode ON for Player [{}] with client version {}.", GetCleanName(), (uint32) ClientVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,6 +930,7 @@ void Client::TraderEndTrader()
|
|||||||
|
|
||||||
WithCustomer(0);
|
WithCustomer(0);
|
||||||
SetTrader(false);
|
SetTrader(false);
|
||||||
|
UpdateWho();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SendTraderItem(uint32 ItemID, uint16 Quantity, TraderRepository::Trader &t) {
|
void Client::SendTraderItem(uint32 ItemID, uint16 Quantity, TraderRepository::Trader &t) {
|
||||||
@ -1402,22 +1406,6 @@ void Client::TradeRequestFailed(TraderBuy_Struct &in)
|
|||||||
QueuePacket(&outapp);
|
QueuePacket(&outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static void BazaarAuditTrail(const char *seller, const char *buyer, const char *itemName, int quantity, int totalCost, int tranType) {
|
|
||||||
//
|
|
||||||
// const std::string& query = fmt::format(
|
|
||||||
// "INSERT INTO `trader_audit` "
|
|
||||||
// "(`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) "
|
|
||||||
// "VALUES (NOW(), '{}', '{}', '{}', {}, {}, {})",
|
|
||||||
// seller,
|
|
||||||
// buyer,
|
|
||||||
// Strings::Escape(itemName),
|
|
||||||
// quantity,
|
|
||||||
// totalCost,
|
|
||||||
// tranType
|
|
||||||
// );
|
|
||||||
// database.QueryDatabase(query);
|
|
||||||
// }
|
|
||||||
|
|
||||||
void Client::BuyTraderItem(const EQApplicationPacket *app)
|
void Client::BuyTraderItem(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
auto in = reinterpret_cast<TraderBuy_Struct *>(app->pBuffer);
|
auto in = reinterpret_cast<TraderBuy_Struct *>(app->pBuffer);
|
||||||
@ -1594,6 +1582,7 @@ void Client::BuyTraderItem(const EQApplicationPacket *app)
|
|||||||
.charges = buy_inst->GetCharges(),
|
.charges = buy_inst->GetCharges(),
|
||||||
.total_cost = total_cost,
|
.total_cost = total_cost,
|
||||||
.player_money_balance = GetCarriedMoney(),
|
.player_money_balance = GetCarriedMoney(),
|
||||||
|
.offline_purchase = trader->IsOffline(),
|
||||||
};
|
};
|
||||||
|
|
||||||
RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e);
|
RecordPlayerEventLog(PlayerEvent::TRADER_PURCHASE, e);
|
||||||
@ -1616,9 +1605,22 @@ void Client::BuyTraderItem(const EQApplicationPacket *app)
|
|||||||
.charges = buy_inst->GetCharges(),
|
.charges = buy_inst->GetCharges(),
|
||||||
.total_cost = total_cost,
|
.total_cost = total_cost,
|
||||||
.player_money_balance = trader->GetCarriedMoney(),
|
.player_money_balance = trader->GetCarriedMoney(),
|
||||||
|
.offline_purchase = trader->IsOffline(),
|
||||||
};
|
};
|
||||||
|
|
||||||
RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADER_SELL, e);
|
RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADER_SELL, e);
|
||||||
|
|
||||||
|
if (trader->IsOffline()) {
|
||||||
|
auto e = CharacterOfflineTransactionsRepository::NewEntity();
|
||||||
|
e.character_id = trader->CharacterID();
|
||||||
|
e.item_name = buy_inst->GetItem()->Name;
|
||||||
|
e.price = total_cost;
|
||||||
|
e.quantity = quantity;
|
||||||
|
e.type = TRADER_TRANSACTION;
|
||||||
|
e.buyer_name = GetCleanName();
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::InsertOne(database, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1972,12 +1974,18 @@ void Client::SellToBuyer(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!DoBarterBuyerChecks(sell_line)) {
|
if (!DoBarterBuyerChecks(sell_line)) {
|
||||||
|
SendBarterBuyerClientMessage(
|
||||||
|
sell_line, Barter_SellerTransactionComplete, Barter_Failure, Barter_Failure
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
if (!DoBarterSellerChecks(sell_line)) {
|
if (!DoBarterSellerChecks(sell_line)) {
|
||||||
|
SendBarterBuyerClientMessage(
|
||||||
|
sell_line, Barter_SellerTransactionComplete, Barter_Failure, Barter_Failure
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
BuyerRepository::UpdateTransactionDate(database, sell_line.buyer_id, time(nullptr));
|
BuyerRepository::UpdateTransactionDate(database, sell_line.buyer_id, time(nullptr));
|
||||||
|
|
||||||
@ -2078,6 +2086,18 @@ void Client::SellToBuyer(const EQApplicationPacket *app)
|
|||||||
RecordPlayerEventLog(PlayerEvent::BARTER_TRANSACTION, e);
|
RecordPlayerEventLog(PlayerEvent::BARTER_TRANSACTION, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buyer->IsOffline()) {
|
||||||
|
auto e = CharacterOfflineTransactionsRepository::NewEntity();
|
||||||
|
e.character_id = buyer->CharacterID();
|
||||||
|
e.item_name = sell_line.item_name;
|
||||||
|
e.price = total_cost;
|
||||||
|
e.quantity = sell_line.seller_quantity;
|
||||||
|
e.type = BUYER_TRANSACTION;
|
||||||
|
e.buyer_name = GetCleanName();
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::InsertOne(database, e);
|
||||||
|
}
|
||||||
|
|
||||||
SendWindowUpdatesToSellerAndBuyer(sell_line);
|
SendWindowUpdatesToSellerAndBuyer(sell_line);
|
||||||
SendBarterBuyerClientMessage(
|
SendBarterBuyerClientMessage(
|
||||||
sell_line,
|
sell_line,
|
||||||
@ -2220,6 +2240,7 @@ void Client::ToggleBuyerMode(bool status)
|
|||||||
SetCustomerID(0);
|
SetCustomerID(0);
|
||||||
SendBuyerMode(true);
|
SendBuyerMode(true);
|
||||||
SendBuyerToBarterWindow(this, Barter_AddToBarterWindow);
|
SendBuyerToBarterWindow(this, Barter_AddToBarterWindow);
|
||||||
|
UpdateWho();
|
||||||
Message(Chat::Yellow, "Barter Mode ON.");
|
Message(Chat::Yellow, "Barter Mode ON.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2232,6 +2253,8 @@ void Client::ToggleBuyerMode(bool status)
|
|||||||
if (!IsInBuyerSpace()) {
|
if (!IsInBuyerSpace()) {
|
||||||
Message(Chat::Red, "You must be in a Barter Stall to start Barter Mode.");
|
Message(Chat::Red, "You must be in a Barter Stall to start Barter Mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateWho();
|
||||||
Message(Chat::Yellow, fmt::format("Barter Mode OFF. Buy lines deactivated.").c_str());
|
Message(Chat::Yellow, fmt::format("Barter Mode OFF. Buy lines deactivated.").c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2793,8 +2816,9 @@ std::string Client::DetermineMoneyString(uint64 cp)
|
|||||||
|
|
||||||
void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app)
|
void Client::BuyTraderItemFromBazaarWindow(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
auto in = reinterpret_cast<TraderBuy_Struct *>(app->pBuffer);
|
auto in = reinterpret_cast<TraderBuy_Struct *>(app->pBuffer);
|
||||||
auto trader_item = TraderRepository::GetItemByItemUniqueNumber(database, in->item_unique_id);
|
auto trader_item = TraderRepository::GetItemByItemUniqueNumber(database, in->item_unique_id);
|
||||||
|
auto offline = AccountRepository::GetAllOfflineStatus(database, trader_item.char_id);
|
||||||
|
|
||||||
LogTradingDetail(
|
LogTradingDetail(
|
||||||
"Packet details: \n"
|
"Packet details: \n"
|
||||||
|
|||||||
@ -62,6 +62,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/skill_caps.h"
|
#include "../common/skill_caps.h"
|
||||||
#include "../common/server_reload_types.h"
|
#include "../common/server_reload_types.h"
|
||||||
#include "queryserv.h"
|
#include "queryserv.h"
|
||||||
|
#include "../common/repositories/account_repository.h"
|
||||||
|
#include "../common/repositories/character_offline_transactions_repository.h"
|
||||||
|
|
||||||
extern EntityList entity_list;
|
extern EntityList entity_list;
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
@ -3876,10 +3878,23 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
.charges = in->item_charges,
|
.charges = in->item_charges,
|
||||||
.total_cost = total_cost,
|
.total_cost = total_cost,
|
||||||
.player_money_balance = trader_pc->GetCarriedMoney(),
|
.player_money_balance = trader_pc->GetCarriedMoney(),
|
||||||
|
.offline_purchase = trader_pc->IsOffline(),
|
||||||
};
|
};
|
||||||
RecordPlayerEventLogWithClient(trader_pc, PlayerEvent::TRADER_SELL, e);
|
RecordPlayerEventLogWithClient(trader_pc, PlayerEvent::TRADER_SELL, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trader_pc->IsOffline()) {
|
||||||
|
auto e = CharacterOfflineTransactionsRepository::NewEntity();
|
||||||
|
e.character_id = trader_pc->CharacterID();
|
||||||
|
e.item_name = in->trader_buy_struct.item_name;
|
||||||
|
e.price = in->trader_buy_struct.price * in->trader_buy_struct.quantity;
|
||||||
|
e.quantity = in->trader_buy_struct.quantity;
|
||||||
|
e.type = TRADER_TRANSACTION;
|
||||||
|
e.buyer_name = in->trader_buy_struct.buyer_name;
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::InsertOne(database, e);
|
||||||
|
}
|
||||||
|
|
||||||
in->transaction_status = BazaarPurchaseSuccess;
|
in->transaction_status = BazaarPurchaseSuccess;
|
||||||
TraderRepository::UpdateActiveTransaction(database, in->id, false);
|
TraderRepository::UpdateActiveTransaction(database, in->id, false);
|
||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
@ -4274,6 +4289,18 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
RecordPlayerEventLogWithClient(buyer, PlayerEvent::BARTER_TRANSACTION, e);
|
RecordPlayerEventLogWithClient(buyer, PlayerEvent::BARTER_TRANSACTION, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buyer->IsOffline()) {
|
||||||
|
auto e = CharacterOfflineTransactionsRepository::NewEntity();
|
||||||
|
e.character_id = buyer->CharacterID();
|
||||||
|
e.item_name = sell_line.item_name;
|
||||||
|
e.price = (uint64) sell_line.item_cost * (uint64) in->seller_quantity;
|
||||||
|
e.quantity = sell_line.seller_quantity;
|
||||||
|
e.type = BUYER_TRANSACTION;
|
||||||
|
e.buyer_name = sell_line.seller_name;
|
||||||
|
|
||||||
|
CharacterOfflineTransactionsRepository::InsertOne(database, e);
|
||||||
|
}
|
||||||
|
|
||||||
in->action = Barter_BuyerTransactionComplete;
|
in->action = Barter_BuyerTransactionComplete;
|
||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
|
|
||||||
@ -4334,8 +4361,103 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_UsertoWorldCancelOfflineRequest: {
|
||||||
|
auto in = reinterpret_cast<UsertoWorldResponse *>(pack->pBuffer);
|
||||||
|
auto client = entity_list.GetClientByLSID(in->lsaccountid);
|
||||||
|
if (!client) {
|
||||||
|
LogLoginserverDetail("Step 6a(1) - Zone received ServerOP_UsertoWorldCancelOfflineRequest though could "
|
||||||
|
"not find client."
|
||||||
|
);
|
||||||
|
|
||||||
|
auto e = AccountRepository::GetWhere(database, fmt::format("`lsaccount_id` = '{}'", in->lsaccountid));
|
||||||
|
if (!e.empty()) {
|
||||||
|
auto r = e.front();
|
||||||
|
r.offline = 0;
|
||||||
|
AccountRepository::UpdateOne(database, r);
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 6a(2) - Zone cleared offline status in account table for user id {} / {}",
|
||||||
|
r.lsaccount_id,
|
||||||
|
r.charname
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto sp = new ServerPacket(ServerOP_UsertoWorldCancelOfflineResponse, pack->size);
|
||||||
|
auto out = reinterpret_cast<UsertoWorldResponse *>(sp->pBuffer);
|
||||||
|
sp->opcode = ServerOP_UsertoWorldCancelOfflineResponse;
|
||||||
|
out->FromID = in->FromID;
|
||||||
|
out->lsaccountid = in->lsaccountid;
|
||||||
|
out->response = in->response;
|
||||||
|
out->ToID = in->ToID;
|
||||||
|
out->worldid = in->worldid;
|
||||||
|
strn0cpy(out->login, in->login, 64);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 6a(3) - Zone sending ServerOP_UsertoWorldCancelOfflineResponse back to world");
|
||||||
|
worldserver.SendPacket(sp);
|
||||||
|
safe_delete(sp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 6b(1) - Zone received ServerOP_UsertoWorldCancelOfflineRequest and found client {}",
|
||||||
|
client->GetCleanName()
|
||||||
|
);
|
||||||
|
LogLoginserverDetail(
|
||||||
|
"Step 6b(2) - Zone cleared offline status in account table for user id {} / {}",
|
||||||
|
client->CharacterID(),
|
||||||
|
client->GetCleanName()
|
||||||
|
);
|
||||||
|
AccountRepository::SetOfflineStatus(database, client->AccountID(), false);
|
||||||
|
|
||||||
|
if (client->IsThereACustomer()) {
|
||||||
|
auto customer = entity_list.GetClientByID(client->GetCustomerID());
|
||||||
|
if (customer) {
|
||||||
|
auto end_session = new EQApplicationPacket(OP_ShopEnd);
|
||||||
|
customer->FastQueuePacket(&end_session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->IsTrader()) {
|
||||||
|
LogLoginserverDetail("Step 6b(3) - Zone ending trader mode for client {}", client->GetCleanName());
|
||||||
|
client->TraderEndTrader();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client->IsBuyer()) {
|
||||||
|
LogLoginserverDetail("Step 6b(4) - Zone ending buyer mode for client {}", client->GetCleanName());
|
||||||
|
client->ToggleBuyerMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 6b(5) - Zone updating UpdateWho(2) for client {}", client->GetCleanName());
|
||||||
|
client->UpdateWho(2);
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket();
|
||||||
|
LogLoginserverDetail("Step 6b(6) - Zone sending despawn packet for client {}", client->GetCleanName());
|
||||||
|
client->CreateDespawnPacket(outapp, false);
|
||||||
|
entity_list.QueueClients(nullptr, outapp, false);
|
||||||
|
safe_delete(outapp);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 6b(7) - Zone removing client from entity_list");
|
||||||
|
entity_list.RemoveMob(client->CastToMob()->GetID());
|
||||||
|
|
||||||
|
auto sp = new ServerPacket(ServerOP_UsertoWorldCancelOfflineResponse, pack->size);
|
||||||
|
auto out = reinterpret_cast<UsertoWorldResponse *>(sp->pBuffer);
|
||||||
|
sp->opcode = ServerOP_UsertoWorldCancelOfflineResponse;
|
||||||
|
out->FromID = in->FromID;
|
||||||
|
out->lsaccountid = in->lsaccountid;
|
||||||
|
out->response = in->response;
|
||||||
|
out->ToID = in->ToID;
|
||||||
|
out->worldid = in->worldid;
|
||||||
|
strn0cpy(out->login, in->login, 64);
|
||||||
|
|
||||||
|
LogLoginserverDetail("Step 6b(8) - Zone sending ServerOP_UsertoWorldCancelOfflineResponse back to world");
|
||||||
|
worldserver.SendPacket(sp);
|
||||||
|
safe_delete(sp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int) pack->opcode, pack->size);
|
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int) pack->opcode, pack->size);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user