Phase 1 Offline Trading

Cleanup and testing
Zone updated builds ok
World updated builds ok

Update guild_base.h
This commit is contained in:
Mitch Freeman
2025-03-22 08:39:53 -03:00
parent a81ec11ea3
commit cf3b9638c9
50 changed files with 1777 additions and 229 deletions
+2
View File
@@ -186,6 +186,7 @@ SET(repositories
repositories/base/base_character_leadership_abilities_repository.h
repositories/base/base_character_material_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_containers_repository.h
repositories/base/base_character_peqzone_flags_repository.h
@@ -382,6 +383,7 @@ SET(repositories
repositories/character_leadership_abilities_repository.h
repositories/character_material_repository.h
repositories/character_memmed_spells_repository.h
repositories/character_offline_transactions_repository.h
repositories/character_parcels_repository.h
repositories/character_parcels_containers_repository.h
repositories/character_peqzone_flags_repository.h
+7 -2
View File
@@ -210,7 +210,7 @@ void Database::LoginIP(uint32 account_id, const std::string& login_ip)
QueryDatabase(query);
}
int16 Database::GetAccountStatus(uint32 account_id)
AccountStatus::StatusRecord Database::GetAccountStatus(uint32 account_id)
{
auto e = AccountRepository::FindOne(*this, account_id);
@@ -222,7 +222,11 @@ int16 Database::GetAccountStatus(uint32 account_id)
AccountRepository::UpdateOne(*this, e);
}
return e.status;
AccountStatus::StatusRecord result{};
result.status = e.status;
result.offline = e.offline;
return result;
}
uint32 Database::CreateAccount(
@@ -2265,6 +2269,7 @@ void Database::ClearGuildOnlineStatus()
void Database::ClearTraderDetails()
{
TraderRepository::Truncate(*this);
AccountRepository::ClearAllOfflineStatus(*this);
}
void Database::ClearBuyerDetails()
+1 -1
View File
@@ -172,8 +172,8 @@ public:
const std::string GetLiveChar(uint32 account_id);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 account_id, const std::string& password);
AccountStatus::StatusRecord GetAccountStatus(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);
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
uint32 CreateAccount(
@@ -7240,6 +7240,32 @@ ALTER TABLE `trader`
)",
.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
// ManifestEntry{
// .version = 9228,
+2
View File
@@ -59,6 +59,7 @@ namespace DatabaseSchema {
{"character_leadership_abilities", "id"},
{"character_material", "id"},
{"character_memmed_spells", "id"},
{"character_offline_transactions", "character_id"},
{"character_parcels", "char_id"},
{"character_parcels_containers", "id"},
{"character_pet_buffs", "char_id"},
@@ -134,6 +135,7 @@ namespace DatabaseSchema {
"character_leadership_abilities",
"character_material",
"character_memmed_spells",
"character_offline_transactions",
"character_parcels",
"character_parcels_containers",
"character_pet_buffs",
+5
View File
@@ -46,6 +46,11 @@ namespace AccountStatus {
constexpr uint8 Max = 255;
std::string GetName(uint8 account_status);
struct StatusRecord {
int16 status;
uint32 offline;
};
}
static std::map<uint8, std::string> account_status_names = {
+3
View File
@@ -71,6 +71,8 @@ N(OP_BuyerItems),
N(OP_CameraEffect),
N(OP_Camp),
N(OP_CancelSneakHide),
N(OP_CancelOfflineTrader),
N(OP_CancelOfflineTraderResponse),
N(OP_CancelTask),
N(OP_CancelTrade),
N(OP_CashReward),
@@ -381,6 +383,7 @@ N(OP_MultiLineMsg),
N(OP_NewSpawn),
N(OP_NewTitlesAvailable),
N(OP_NewZone),
N(OP_Offline),
N(OP_OnLevelMessage),
N(OP_OpenContainer),
N(OP_OpenDiscordMerchant),
+8 -6
View File
@@ -326,6 +326,7 @@ union
bool buyer;
bool untargetable;
uint32 npc_tint_id;
bool offline;
};
struct PlayerState_Struct {
@@ -3913,12 +3914,12 @@ struct SimpleMessage_Struct{
};
struct GuildMemberUpdate_Struct {
/*00*/ uint32 GuildID;
/*04*/ char MemberName[64];
/*68*/ uint16 ZoneID;
/*70*/ uint16 InstanceID; //speculated
/*72*/ uint32 LastSeen; //unix timestamp
/*76*/
/*00*/ uint32 GuildID;
/*04*/ char MemberName[64];
/*68*/ uint16 ZoneID;
/*72*/ uint16 InstanceID; //speculated
/*76*/ uint32 LastSeen; //unix timestamp
/*80*/ uint32 offline_mode;
};
struct GuildMemberLevelUpdate_Struct {
@@ -3941,6 +3942,7 @@ struct Internal_GuildMemberEntry_Struct {
uint16 zoneinstance; //network byte order
uint16 zone_id; //network byte order
uint32 online;
uint32 offline_mode;
};
struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding.
+7 -2
View File
@@ -1098,6 +1098,7 @@ namespace PlayerEvent {
int32 charges;
uint64 total_cost;
uint64 player_money_balance;
bool offline_purchase;
// cereal
template <class Archive>
@@ -1153,7 +1154,8 @@ namespace PlayerEvent {
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
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;
uint64 total_cost;
uint64 player_money_balance;
bool offline_purchase;
// cereal
template <class Archive>
void serialize(Archive& ar)
{
@@ -1228,7 +1232,8 @@ namespace PlayerEvent {
CEREAL_NVP(quantity),
CEREAL_NVP(charges),
CEREAL_NVP(total_cost),
CEREAL_NVP(player_money_balance)
CEREAL_NVP(player_money_balance),
CEREAL_NVP(offline_purchase)
);
}
};
+5 -2
View File
@@ -844,8 +844,10 @@ bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg)
#define GuildMemberBaseQuery \
"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.`banker`, g.`public_note`, g.`alt`, g.`online` " \
" FROM `character_data` AS c LEFT JOIN `guild_members` AS g ON c.`id` = g.`char_id` "
" 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` " \
" LEFT JOIN `account` AS a ON a.`id` = c.`account_id` "
static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into)
{
//fields from `characer_`
@@ -866,6 +868,7 @@ static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into)
into.public_note = row[12] ? row[12] : "";
into.alt = row[13] ? (row[13][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
if (into.guild_id == 0) {
+11 -10
View File
@@ -55,16 +55,17 @@ class CharGuildInfo
uint32 time_last_on;
uint32 zone_id;
//fields from `guild_members`
uint32 guild_id;
uint8 rank;
bool tribute_enable;
uint32 total_tribute;
uint32 last_tribute; //timestamp
bool banker;
bool alt;
std::string public_note;
bool online;
// fields from `guild_members`
uint32 guild_id;
uint8 rank;
bool tribute_enable;
uint32 total_tribute;
uint32 last_tribute; // timestamp
bool banker;
bool alt;
std::string public_note;
bool online;
bool offline_mode;
};
//this object holds guild functionality shared between world and zone.
+15 -11
View File
@@ -1845,7 +1845,7 @@ namespace RoF2
e->zoneinstance = 0;
e->zone_id = htons(emu_e->zone_id);
e->unknown_one2 = htonl(1);
e->unknown04 = 0;
e->offline_mode = htonl(emu_e->offline_mode);
#undef SlideStructString
#undef PutFieldN
@@ -1862,14 +1862,12 @@ namespace RoF2
{
SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct);
OUT(GuildID);
memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName));
//OUT(ZoneID);
//OUT(InstanceID);
eq->InstanceID = emu->InstanceID;
eq->ZoneID = emu->ZoneID;
OUT(LastSeen);
eq->Unknown76 = 0;
eq->guild_id = emu->GuildID;
eq->last_seen = emu->LastSeen;
eq->instance_id = emu->InstanceID;
eq->zone_id = emu->ZoneID;
eq->offline_mode = emu->offline_mode;
memcpy(eq->member_name, emu->MemberName, sizeof(eq->member_name));
FINISH_ENCODE();
}
@@ -4475,6 +4473,7 @@ namespace RoF2
*p = nullptr;
char *InBuffer = (char *)in->pBuffer;
std::vector<uint32> p_ids { 0x430, 0x420 };
WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer;
@@ -4500,8 +4499,9 @@ namespace RoF2
x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x);
InBuffer += 4;
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0);
x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer);
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);
char Name[64];
@@ -4737,6 +4737,10 @@ namespace RoF2
OtherData = OtherData | 0x01;
}
if (emu->offline) {
OtherData = OtherData | 0x02;
}
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
// float EmitterScalingRadius
+22 -23
View File
@@ -3681,22 +3681,22 @@ struct SimpleMessage_Struct{
// Size: 52 + strings
// Other than the strings, all of this packet is network byte order (reverse from normal)
struct GuildMemberEntry_Struct {
char name[1]; // variable length
uint32 level;
uint32 banker; // 1=yes, 0=no
uint32 class_;
uint32 rank;
uint32 time_last_on;
uint32 tribute_enable;
uint32 unknown01; // Seen 0
uint32 total_tribute; // total guild tribute donated, network byte order
uint32 last_tribute; // unix timestamp
uint32 unknown_one; // unknown, set to 1
char public_note[1]; // variable length.
uint16 zoneinstance; // Seen 0s or -1 in RoF2
uint16 zone_id; // Seen 0s or -1 in RoF2
uint32 unknown_one2; // unknown, set to 1
uint32 unknown04; // Seen 0
char name[1]; // variable length
uint32 level;
uint32 banker; // 1=yes, 0=no
uint32 class_;
uint32 rank;
uint32 time_last_on;
uint32 tribute_enable;
uint32 unknown01; // Seen 0
uint32 total_tribute; // total guild tribute donated, network byte order
uint32 last_tribute; // unix timestamp
uint32 unknown_one; // unknown, set to 1
char public_note[1]; // variable length.
uint16 zoneinstance; // Seen 0s or -1 in RoF2
uint16 zone_id; // Seen 0s or -1 in RoF2
uint32 unknown_one2; // unknown, set to 1
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.
@@ -3731,13 +3731,12 @@ struct GuildStatus_Struct
};
struct GuildMemberUpdate_Struct {
/*00*/ uint32 GuildID;
/*04*/ char MemberName[64];
/*68*/ uint16 ZoneID;
/*70*/ uint16 InstanceID; //speculated
/*72*/ uint32 LastSeen; //unix timestamp
/*76*/ uint32 Unknown76;
/*80*/
/*00*/ uint32 guild_id;
/*04*/ char member_name[64];
/*68*/ uint16 zone_id;
/*70*/ uint16 instance_id; //speculated
/*72*/ uint32 last_seen; //unix timestamp
/*76*/ uint32 offline_mode;
};
struct GuildMemberLevelUpdate_Struct {
+39
View File
@@ -107,6 +107,45 @@ public:
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
@@ -39,6 +39,7 @@ public:
uint8_t rulesflag;
time_t suspendeduntil;
uint32_t time_creation;
uint8_t offline;
std::string ban_reason;
std::string suspend_reason;
std::string crc_eqgame;
@@ -74,6 +75,7 @@ public:
"rulesflag",
"suspendeduntil",
"time_creation",
"offline",
"ban_reason",
"suspend_reason",
"crc_eqgame",
@@ -105,6 +107,7 @@ public:
"rulesflag",
"UNIX_TIMESTAMP(suspendeduntil)",
"time_creation",
"offline",
"ban_reason",
"suspend_reason",
"crc_eqgame",
@@ -170,6 +173,7 @@ public:
e.rulesflag = 0;
e.suspendeduntil = 0;
e.time_creation = 0;
e.offline = 0;
e.ban_reason = "";
e.suspend_reason = "";
e.crc_eqgame = "";
@@ -231,11 +235,12 @@ public:
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.time_creation = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.ban_reason = row[20] ? row[20] : "";
e.suspend_reason = row[21] ? row[21] : "";
e.crc_eqgame = row[22] ? row[22] : "";
e.crc_skillcaps = row[23] ? row[23] : "";
e.crc_basedata = row[24] ? row[24] : "";
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
e.ban_reason = row[21] ? row[21] : "";
e.suspend_reason = row[22] ? row[22] : "";
e.crc_eqgame = row[23] ? row[23] : "";
e.crc_skillcaps = row[24] ? row[24] : "";
e.crc_basedata = row[25] ? row[25] : "";
return e;
}
@@ -288,11 +293,12 @@ public:
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[19] + " = " + std::to_string(e.time_creation));
v.push_back(columns[20] + " = '" + Strings::Escape(e.ban_reason) + "'");
v.push_back(columns[21] + " = '" + Strings::Escape(e.suspend_reason) + "'");
v.push_back(columns[22] + " = '" + Strings::Escape(e.crc_eqgame) + "'");
v.push_back(columns[23] + " = '" + Strings::Escape(e.crc_skillcaps) + "'");
v.push_back(columns[24] + " = '" + Strings::Escape(e.crc_basedata) + "'");
v.push_back(columns[20] + " = " + std::to_string(e.offline));
v.push_back(columns[21] + " = '" + Strings::Escape(e.ban_reason) + "'");
v.push_back(columns[22] + " = '" + Strings::Escape(e.suspend_reason) + "'");
v.push_back(columns[23] + " = '" + Strings::Escape(e.crc_eqgame) + "'");
v.push_back(columns[24] + " = '" + Strings::Escape(e.crc_skillcaps) + "'");
v.push_back(columns[25] + " = '" + Strings::Escape(e.crc_basedata) + "'");
auto results = db.QueryDatabase(
fmt::format(
@@ -334,6 +340,7 @@ public:
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(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.suspend_reason) + "'");
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
@@ -388,6 +395,7 @@ public:
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(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.suspend_reason) + "'");
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.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.ban_reason = row[20] ? row[20] : "";
e.suspend_reason = row[21] ? row[21] : "";
e.crc_eqgame = row[22] ? row[22] : "";
e.crc_skillcaps = row[23] ? row[23] : "";
e.crc_basedata = row[24] ? row[24] : "";
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
e.ban_reason = row[21] ? row[21] : "";
e.suspend_reason = row[22] ? row[22] : "";
e.crc_eqgame = row[23] ? row[23] : "";
e.crc_skillcaps = row[24] ? row[24] : "";
e.crc_basedata = row[25] ? row[25] : "";
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.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.ban_reason = row[20] ? row[20] : "";
e.suspend_reason = row[21] ? row[21] : "";
e.crc_eqgame = row[22] ? row[22] : "";
e.crc_skillcaps = row[23] ? row[23] : "";
e.crc_basedata = row[24] ? row[24] : "";
e.offline = row[20] ? static_cast<uint8_t>(strtoul(row[20], nullptr, 10)) : 0;
e.ban_reason = row[21] ? row[21] : "";
e.suspend_reason = row[22] ? row[22] : "";
e.crc_eqgame = row[23] ? row[23] : "";
e.crc_skillcaps = row[24] ? row[24] : "";
e.crc_basedata = row[25] ? row[25] : "";
all_entries.push_back(e);
}
@@ -594,6 +604,7 @@ public:
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(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.suspend_reason) + "'");
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
@@ -641,6 +652,7 @@ public:
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(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.suspend_reason) + "'");
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
+27 -2
View File
@@ -106,8 +106,13 @@ public:
return false;
}
auto buy_lines =
BaseBuyerBuyLinesRepository::GetWhere(db, fmt::format("`buyer_id` = {}", buyer.front().id));
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
db,
fmt::format("`buyer_id` = '{}'", buyer.front().id)
);
if (buy_lines.empty()) {
return false;
}
std::vector<std::string> buy_line_ids{};
for (auto const &bl: buy_lines) {
@@ -175,6 +180,26 @@ public:
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
@@ -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
+43
View File
@@ -395,6 +395,49 @@ public:
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
+17 -10
View File
@@ -229,10 +229,12 @@
#define ServerOP_LSPlayerJoinWorld 0x3007
#define ServerOP_LSPlayerZoneChange 0x3008
#define ServerOP_UsertoWorldReqLeg 0xAB00
#define ServerOP_UsertoWorldRespLeg 0xAB01
#define ServerOP_UsertoWorldReq 0xAB02
#define ServerOP_UsertoWorldResp 0xAB03
#define ServerOP_UsertoWorldReqLeg 0xAB00
#define ServerOP_UsertoWorldRespLeg 0xAB01
#define ServerOP_UsertoWorldReq 0xAB02
#define ServerOP_UsertoWorldResp 0xAB03
#define ServerOP_UsertoWorldCancelOfflineRequest 0xAB04
#define ServerOP_UsertoWorldCancelOfflineResponse 0xAB05
#define ServerOP_LauncherConnectInfo 0x3000
#define ServerOP_LauncherZoneRequest 0x3001
@@ -360,12 +362,13 @@ enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_
enum {
UserToWorldStatusWorldUnavail = 0,
UserToWorldStatusSuccess = 1,
UserToWorldStatusSuspended = -1,
UserToWorldStatusBanned = -2,
UserToWorldStatusWorldAtCapacity = -3,
UserToWorldStatusAlreadyOnline = -4
UserToWorldStatusWorldUnavail = 0,
UserToWorldStatusSuccess = 1,
UserToWorldStatusSuspended = -1,
UserToWorldStatusBanned = -2,
UserToWorldStatusWorldAtCapacity = -3,
UserToWorldStatusAlreadyOnline = -4,
UserToWorldStatusOffilineTraderBuyer = -5
};
enum {
@@ -567,6 +570,9 @@ struct ServerClientList_Struct {
uint8 LFGToLevel;
bool LFGMatchFilter;
char LFGComments[64];
bool trader;
bool buyer;
bool offline;
};
struct ServerClientListKeepAlive_Struct {
@@ -1030,6 +1036,7 @@ struct ServerGuildMemberUpdate_Struct {
char member_name[64];
uint32 zone_id;
uint32 last_seen;
uint32 offline_mode;
};
struct ServerGuildPermissionUpdate_Struct {