mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat: store each named pet's LOT in pet_names
pet_names gets a pet_lot column (mysql 80, sqlite 63). The world writes it whenever it saves a pet name, from the pet entity's LOT, and fills it in for older rows when the owner loads into a world (from the pets the game loads for that character, only where it is still missing). The dashboard's pet name tables read pet_lot instead of scanning the owner's character XML; pets without it yet show as Unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,24 +90,13 @@ namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds each pet's LOT and kind (its CDClient name) to pet_names rows. pet_names doesn't hold the LOT; the owner's
|
||||
* save does (<pet><p id l/>), so each owner on the page is read once. Pets whose owner is unknown, or who have
|
||||
* left the owner's save, get lot 0.
|
||||
* Adds each pet's kind (its CDClient name) to pet_names rows, from the pet_lot the game writes (lot 0: not known yet).
|
||||
*/
|
||||
std::string WithPetKinds(const std::string& raw) {
|
||||
auto json = nlohmann::json::parse(raw, nullptr, false);
|
||||
if (json.is_discarded() || !json.contains("data") || !json["data"].is_array()) return raw;
|
||||
std::map<LWOOBJID, std::map<LWOOBJID, LOT>> owners;
|
||||
for (auto& row : json["data"]) {
|
||||
const auto owner = GeneralUtils::TryParse<LWOOBJID>(row.value("owner_id", std::string{})).value_or(0);
|
||||
const auto pet = GeneralUtils::TryParse<LWOOBJID>(row.value("id", std::string{})).value_or(0);
|
||||
LOT lot = 0;
|
||||
if (owner && pet) {
|
||||
auto [it, added] = owners.try_emplace(owner);
|
||||
if (added) for (const auto& held : CharacterXml::Pets(Database::Get()->GetCharacterXml(owner))) it->second[held.id] = held.lot;
|
||||
if (const auto found = it->second.find(pet); found != it->second.end()) lot = found->second;
|
||||
}
|
||||
row["lot"] = lot;
|
||||
const LOT lot = row.value("lot", 0);
|
||||
row["kind"] = lot > 0 ? ClientAssets::ItemName(lot) : "";
|
||||
}
|
||||
return json.dump();
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
},
|
||||
// A pet's kind: its icon and CDClient name (rows from the pet name tables carry lot and kind)
|
||||
pet: function (lot, kind) {
|
||||
if (!lot) return '<span class="text-body-secondary small" title="The owner\'s save doesn\'t hold this pet">Unknown</span>';
|
||||
if (!lot) return '<span class="text-body-secondary small" title="Not recorded yet: filled in when the owner next loads into a world">Unknown</span>';
|
||||
return '<span class="text-nowrap"><img src="/api/icon/' + esc(lot) + '" width="28" height="28" class="me-1 align-middle" alt="" loading="lazy">' +
|
||||
esc(kind || ('LOT ' + lot)) + ' <span class="small text-body-secondary">(' + esc(lot) + ')</span></span>';
|
||||
},
|
||||
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
int32_t approvalStatus{};
|
||||
// The character that owns the pet (0 when not known). Saving with 0 keeps the owner already stored.
|
||||
LWOOBJID ownerId{};
|
||||
// The pet's LOT (0 when not known). Saving with 0 keeps the LOT already stored.
|
||||
LOT petLot{};
|
||||
};
|
||||
|
||||
// Set the pet name moderation status for the given pet id.
|
||||
@@ -36,6 +38,9 @@ public:
|
||||
// Pets whose owner was never recorded (named before owners were saved), and recording one (0: nobody has it)
|
||||
virtual std::vector<LWOOBJID> GetPetsWithUnknownOwner() = 0;
|
||||
virtual void SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) = 0;
|
||||
|
||||
// Record the pet's LOT on its pet_names row, only when the row has none yet (NULL or 0). Does nothing without a row.
|
||||
virtual void SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IPETNAMES__H__
|
||||
|
||||
@@ -406,6 +406,7 @@ public:
|
||||
void RejectPetName(const int64_t id) override;
|
||||
std::vector<LWOOBJID> GetPetsWithUnknownOwner() override;
|
||||
void SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) override;
|
||||
void SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) override;
|
||||
nlohmann::json GetAccountCharacters(uint32_t accountId) override;
|
||||
void DeleteAccount(const uint32_t accountId) override;
|
||||
void InsertAuditLog(uint32_t accountId, const std::string_view accountName, const std::string_view action, const std::string_view description, uint32_t targetAccountId, LWOOBJID targetCharacterId) override;
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
|
||||
void MySQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) {
|
||||
const auto owner = info.ownerId == 0 ? std::optional<LWOOBJID>{} : std::optional<LWOOBJID>{ info.ownerId };
|
||||
const auto lot = info.petLot <= 0 ? std::optional<uint32_t>{} : std::optional<uint32_t>{ static_cast<uint32_t>(info.petLot) };
|
||||
ExecuteInsert(
|
||||
"INSERT INTO `pet_names` (`id`, `pet_name`, `approved`, `owner_id`) VALUES (?, ?, ?, ?) "
|
||||
"ON DUPLICATE KEY UPDATE pet_name = ?, approved = ?, owner_id = COALESCE(?, owner_id);",
|
||||
"INSERT INTO `pet_names` (`id`, `pet_name`, `approved`, `owner_id`, `pet_lot`) VALUES (?, ?, ?, ?, ?) "
|
||||
"ON DUPLICATE KEY UPDATE pet_name = ?, approved = ?, owner_id = COALESCE(?, owner_id), pet_lot = COALESCE(?, pet_lot);",
|
||||
petId,
|
||||
info.petName,
|
||||
info.approvalStatus,
|
||||
owner,
|
||||
lot,
|
||||
info.petName,
|
||||
info.approvalStatus,
|
||||
owner);
|
||||
owner,
|
||||
lot);
|
||||
}
|
||||
|
||||
std::optional<IPetNames::Info> MySQLDatabase::GetPetNameInfo(const LWOOBJID& petId) {
|
||||
auto result = ExecuteSelect("SELECT pet_name, approved, owner_id FROM pet_names WHERE id = ? LIMIT 1;", petId);
|
||||
auto result = ExecuteSelect("SELECT pet_name, approved, owner_id, pet_lot FROM pet_names WHERE id = ? LIMIT 1;", petId);
|
||||
|
||||
if (!result->next()) {
|
||||
return std::nullopt;
|
||||
@@ -27,6 +30,7 @@ std::optional<IPetNames::Info> MySQLDatabase::GetPetNameInfo(const LWOOBJID& pet
|
||||
toReturn.petName = result->getString("pet_name").c_str();
|
||||
toReturn.approvalStatus = result->getInt("approved");
|
||||
toReturn.ownerId = result->isNull("owner_id") ? 0 : result->getInt64("owner_id");
|
||||
toReturn.petLot = result->isNull("pet_lot") ? 0 : result->getInt("pet_lot");
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
@@ -51,7 +55,7 @@ std::string MySQLDatabase::GetPetNamesTable(uint32_t start, uint32_t length, con
|
||||
case 3: orderColumnName = "c.name"; break;
|
||||
}
|
||||
std::string orderClause = " ORDER BY " + orderColumnName + (orderAsc ? " ASC" : " DESC");
|
||||
std::string mainQuery = "SELECT p.id, p.pet_name, p.approved, p.owner_id, c.name AS owner_name" + from + whereClause + orderClause + " LIMIT ?, ?;";
|
||||
std::string mainQuery = "SELECT p.id, p.pet_name, p.approved, p.owner_id, p.pet_lot, c.name AS owner_name" + from + whereClause + orderClause + " LIMIT ?, ?;";
|
||||
|
||||
auto totalCountResult = ExecuteSelect("SELECT COUNT(*) as count FROM pet_names p" + (pendingOnly ? " WHERE " + pendingFilter : std::string{}) + ";");
|
||||
uint32_t totalRecords = totalCountResult->next() ? totalCountResult->getUInt("count") : 0;
|
||||
@@ -74,6 +78,7 @@ std::string MySQLDatabase::GetPetNamesTable(uint32_t start, uint32_t length, con
|
||||
{"pet_name", result->getString("pet_name")},
|
||||
{"approved", result->getInt("approved")},
|
||||
{"owner_id", hasOwner ? std::to_string(result->getInt64("owner_id")) : ""},
|
||||
{"lot", result->isNull("pet_lot") ? 0 : result->getInt("pet_lot")},
|
||||
{"owner_name", std::string(result->isNull("owner_name") ? "" : result->getString("owner_name").c_str())}
|
||||
});
|
||||
}
|
||||
@@ -98,3 +103,8 @@ std::vector<LWOOBJID> MySQLDatabase::GetPetsWithUnknownOwner() {
|
||||
void MySQLDatabase::SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) {
|
||||
ExecuteUpdate("UPDATE pet_names SET owner_id = ? WHERE id = ?;", ownerId, petId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) {
|
||||
if (petLot <= 0) return;
|
||||
ExecuteUpdate("UPDATE pet_names SET pet_lot = ? WHERE id = ? AND (pet_lot IS NULL OR pet_lot = 0);", petLot, petId);
|
||||
}
|
||||
|
||||
@@ -390,6 +390,7 @@ public:
|
||||
void RejectPetName(const int64_t id) override;
|
||||
std::vector<LWOOBJID> GetPetsWithUnknownOwner() override;
|
||||
void SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) override;
|
||||
void SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) override;
|
||||
nlohmann::json GetAccountCharacters(uint32_t accountId) override;
|
||||
void DeleteAccount(const uint32_t accountId) override;
|
||||
void InsertAuditLog(uint32_t accountId, const std::string_view accountName, const std::string_view action, const std::string_view description, uint32_t targetAccountId, LWOOBJID targetCharacterId) override;
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
|
||||
void SQLiteDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) {
|
||||
const auto owner = info.ownerId == 0 ? std::optional<LWOOBJID>{} : std::optional<LWOOBJID>{ info.ownerId };
|
||||
const auto lot = info.petLot <= 0 ? std::optional<uint32_t>{} : std::optional<uint32_t>{ static_cast<uint32_t>(info.petLot) };
|
||||
ExecuteInsert(
|
||||
"INSERT INTO `pet_names` (`id`, `pet_name`, `approved`, `owner_id`) VALUES (?, ?, ?, ?) "
|
||||
"ON CONFLICT(id) DO UPDATE SET pet_name = ?, approved = ?, owner_id = COALESCE(?, owner_id);",
|
||||
"INSERT INTO `pet_names` (`id`, `pet_name`, `approved`, `owner_id`, `pet_lot`) VALUES (?, ?, ?, ?, ?) "
|
||||
"ON CONFLICT(id) DO UPDATE SET pet_name = ?, approved = ?, owner_id = COALESCE(?, owner_id), pet_lot = COALESCE(?, pet_lot);",
|
||||
petId,
|
||||
info.petName,
|
||||
info.approvalStatus,
|
||||
owner,
|
||||
lot,
|
||||
info.petName,
|
||||
info.approvalStatus,
|
||||
owner);
|
||||
owner,
|
||||
lot);
|
||||
}
|
||||
|
||||
std::optional<IPetNames::Info> SQLiteDatabase::GetPetNameInfo(const LWOOBJID& petId) {
|
||||
auto [_, result] = ExecuteSelect("SELECT pet_name, approved, owner_id FROM pet_names WHERE id = ? LIMIT 1;", petId);
|
||||
auto [_, result] = ExecuteSelect("SELECT pet_name, approved, owner_id, pet_lot FROM pet_names WHERE id = ? LIMIT 1;", petId);
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
@@ -27,6 +30,7 @@ std::optional<IPetNames::Info> SQLiteDatabase::GetPetNameInfo(const LWOOBJID& pe
|
||||
toReturn.petName = result.getStringField("pet_name");
|
||||
toReturn.approvalStatus = result.getIntField("approved");
|
||||
toReturn.ownerId = result.fieldIsNull("owner_id") ? 0 : result.getInt64Field("owner_id");
|
||||
toReturn.petLot = result.fieldIsNull("pet_lot") ? 0 : result.getIntField("pet_lot");
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
@@ -51,7 +55,7 @@ std::string SQLiteDatabase::GetPetNamesTable(uint32_t start, uint32_t length, co
|
||||
case 3: orderColumnName = "c.name"; break;
|
||||
}
|
||||
std::string orderClause = " ORDER BY " + orderColumnName + (orderAsc ? " ASC" : " DESC");
|
||||
std::string mainQuery = "SELECT p.id, p.pet_name, p.approved, p.owner_id, c.name AS owner_name" + from + whereClause + orderClause + " LIMIT ? OFFSET ?;";
|
||||
std::string mainQuery = "SELECT p.id, p.pet_name, p.approved, p.owner_id, p.pet_lot, c.name AS owner_name" + from + whereClause + orderClause + " LIMIT ? OFFSET ?;";
|
||||
|
||||
auto [__, totalCountResult] = ExecuteSelect("SELECT COUNT(*) as count FROM pet_names p" + (pendingOnly ? " WHERE " + pendingFilter : std::string{}) + ";");
|
||||
uint32_t totalRecords = totalCountResult.eof() ? 0 : totalCountResult.getIntField("count");
|
||||
@@ -74,6 +78,7 @@ std::string SQLiteDatabase::GetPetNamesTable(uint32_t start, uint32_t length, co
|
||||
{"pet_name", result.getStringField("pet_name")},
|
||||
{"approved", result.getIntField("approved")},
|
||||
{"owner_id", hasOwner ? std::to_string(result.getInt64Field("owner_id")) : ""},
|
||||
{"lot", result.fieldIsNull("pet_lot") ? 0 : result.getIntField("pet_lot")},
|
||||
{"owner_name", std::string(result.fieldIsNull("owner_name") ? "" : result.getStringField("owner_name"))}
|
||||
});
|
||||
result.nextRow();
|
||||
@@ -99,3 +104,8 @@ std::vector<LWOOBJID> SQLiteDatabase::GetPetsWithUnknownOwner() {
|
||||
void SQLiteDatabase::SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) {
|
||||
ExecuteUpdate("UPDATE pet_names SET owner_id = ? WHERE id = ?;", ownerId, petId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) {
|
||||
if (petLot <= 0) return;
|
||||
ExecuteUpdate("UPDATE pet_names SET pet_lot = ? WHERE id = ? AND (pet_lot IS NULL OR pet_lot = 0);", petLot, petId);
|
||||
}
|
||||
|
||||
@@ -364,6 +364,7 @@ class TestSQLDatabase : public GameDatabase {
|
||||
void RejectPetName(const int64_t id) override {};
|
||||
std::vector<LWOOBJID> GetPetsWithUnknownOwner() override { return {}; }
|
||||
void SetPetOwner(const LWOOBJID petId, const LWOOBJID ownerId) override {}
|
||||
void SetPetLotIfMissing(const LWOOBJID petId, const LOT petLot) override {}
|
||||
nlohmann::json GetAccountCharacters(uint32_t accountId) override { return nlohmann::json::array(); };
|
||||
void ApproveProperty(const LWOOBJID propertyId) override {};
|
||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||
|
||||
@@ -1731,9 +1731,9 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
|
||||
auto* petElement = petInventoryElement->FirstChildElement();
|
||||
|
||||
while (petElement != nullptr) {
|
||||
LWOOBJID id;
|
||||
LOT lot;
|
||||
int32_t moderationStatus;
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
int32_t moderationStatus{};
|
||||
|
||||
petElement->QueryAttribute("id", &id);
|
||||
petElement->QueryAttribute("l", &lot);
|
||||
@@ -1747,6 +1747,9 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
|
||||
|
||||
SetDatabasePet(id, databasePet);
|
||||
|
||||
// Pets named before pet_names recorded their LOT get it now that their owner is loaded
|
||||
if (id != LWOOBJID_EMPTY && lot > 0) Database::Get()->SetPetLotIfMissing(id, lot);
|
||||
|
||||
petElement = petElement->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
if (autoRejectNames && owner && owner->GetCharacter() && owner->GetCharacter()->GetParentUser()->GetIsMuted()) {
|
||||
m_ModerationStatus = 2; // Approved
|
||||
std::string forcedName = "Pet";
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus), m_Owner });
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus), m_Owner, m_Parent->GetLOT() });
|
||||
DashboardNotify::Changed("pet_names", m_DatabaseId);
|
||||
{
|
||||
GameMessages::SetPetName msg;
|
||||
@@ -678,7 +678,7 @@ void PetComponent::RequestSetPetName(std::u16string name) {
|
||||
m_Name = "";
|
||||
std::string forcedName = "Pet";
|
||||
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus), m_Tamer });
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ forcedName, static_cast<int32_t>(m_ModerationStatus), m_Tamer, m_Parent->GetLOT() });
|
||||
DashboardNotify::Changed("pet_names", m_DatabaseId);
|
||||
LOG("AccountID: %i is muted, forcing use of predefined pet name", tamer->GetCharacter()->GetParentUser()->GetAccountID());
|
||||
} else {
|
||||
@@ -1235,7 +1235,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName, const LWO
|
||||
}
|
||||
|
||||
//Save to db:
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved, owner });
|
||||
Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved, owner, m_Parent->GetLOT() });
|
||||
DashboardNotify::Changed("pet_names", m_DatabaseId);
|
||||
}
|
||||
|
||||
|
||||
@@ -725,8 +725,9 @@ tells the player in game if they're online. Rejecting asks for an optional reaso
|
||||
account page next to what they asked for. The queues show only what the game itself would still review: rejected
|
||||
properties and names whose player must pick a new one no longer appear.
|
||||
|
||||
Pet names show what kind of pet each one is (its icon and name from the CDClient), read from the pet's entry in its
|
||||
owner's save. A pet whose owner is unknown or no longer has it shows as Unknown.
|
||||
Pet names show what kind of pet each one is (its icon and name from the CDClient), from the pet's LOT that the game
|
||||
stores with the name (`pet_names.pet_lot`). Names set before that column existed get their LOT the next time the pet's
|
||||
owner loads into a world; until then they show as Unknown.
|
||||
|
||||
### Moderation history, warnings and bans
|
||||
|
||||
|
||||
7
migrations/dlu/mysql/80_pet_name_lot.sql
Normal file
7
migrations/dlu/mysql/80_pet_name_lot.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Which kind of pet (its LOT) each named pet is, written by the game when the name is set and filled in for older
|
||||
rows when the owner next loads into a world. NULL or 0: not known yet. Only added when it is not there yet. */
|
||||
SET @dlu_column = (SELECT IF(COUNT(*) = 0, 'ALTER TABLE pet_names ADD COLUMN pet_lot INT NULL DEFAULT NULL', 'DO 0') FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE() AND table_name = 'pet_names' AND column_name = 'pet_lot');
|
||||
PREPARE dlu_column_stmt FROM @dlu_column;
|
||||
EXECUTE dlu_column_stmt;
|
||||
DEALLOCATE PREPARE dlu_column_stmt;
|
||||
2
migrations/dlu/sqlite/63_pet_name_lot.sql
Normal file
2
migrations/dlu/sqlite/63_pet_name_lot.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
/* Which kind of pet (its LOT) each named pet is. See the MySQL migration. */
|
||||
ALTER TABLE pet_names ADD COLUMN pet_lot INTEGER NULL DEFAULT NULL;
|
||||
@@ -89,7 +89,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ICharInfo::Info, name, pendingName, id, accou
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IProperty::Info, name, description, rejectionReason, id, ownerId, cloneId, privacyOption, modApproved, lastUpdatedTime, claimedTime, reputation, performanceCost, zoneId);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IProperty::ShowcaseEntry, info, ownerName, modelCount);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IProperty::ShowcaseResult, total, entries);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IPetNames::Info, petName, approvalStatus, ownerId);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IPetNames::Info, petName, approvalStatus, ownerId, petLot);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ILeaderboard::Entry, charId, lastPlayedTimestamp, primaryScore, secondaryScore, tertiaryScore, numWins, numTimesPlayed, ranking, name);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ILeaderboard::Score, primaryScore, secondaryScore, tertiaryScore);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ILiveOps::LiveEvent, id, type, title, message, zones, instanceId, config, startsAt, endsAt, state, endedAt, endReason, createdBy, endedBy);
|
||||
@@ -604,11 +604,17 @@ TEST_F(ParitySeeded, BugReports) {
|
||||
|
||||
TEST_F(ParitySeeded, PetNames) {
|
||||
Both("SetPetNameModerationStatus", [](GameDatabase& db) {
|
||||
db.SetPetNameModerationStatus(PET1, { "Sparky", 1, CHAR_BOB });
|
||||
db.SetPetNameModerationStatus(PET2, { "Sparky", 2, CHAR_ALICE });
|
||||
db.SetPetNameModerationStatus(PET1, { "Sparky", 1, CHAR_BOB, 3520 });
|
||||
db.SetPetNameModerationStatus(PET2, { "Sparky", 2, CHAR_ALICE, 12432 });
|
||||
db.SetPetNameModerationStatus(PET3, { "Rex", 1, 0 });
|
||||
});
|
||||
Both("SetPetNameModerationStatus keeps owner", [](GameDatabase& db) { db.SetPetNameModerationStatus(PET1, { "Sparky", 1, 0 }); return db.GetPetNameInfo(PET1); });
|
||||
Both("SetPetNameModerationStatus keeps owner and lot", [](GameDatabase& db) { db.SetPetNameModerationStatus(PET1, { "Sparky", 1, 0 }); return db.GetPetNameInfo(PET1); });
|
||||
Both("SetPetLotIfMissing", [](GameDatabase& db) {
|
||||
db.SetPetLotIfMissing(PET2, 3520); // PET2 already has its LOT: kept
|
||||
db.SetPetLotIfMissing(PET3, 12434); // PET3 has none yet: written
|
||||
db.SetPetLotIfMissing(5, 3520); // no row: nothing
|
||||
return json{ db.GetPetNameInfo(PET2), db.GetPetNameInfo(PET3), db.GetPetNameInfo(5) };
|
||||
});
|
||||
Both("GetPetNameInfo", [](GameDatabase& db) { return json{ db.GetPetNameInfo(PET2), db.GetPetNameInfo(PET3), db.GetPetNameInfo(5) }; });
|
||||
Both("GetPetNamesTable", [](GameDatabase& db) { return db.GetPetNamesTable(0, 10); });
|
||||
Both("GetPetNamesTable pending", [](GameDatabase& db) { return db.GetPetNamesTable(0, 10, "", 0, true, true); });
|
||||
|
||||
Reference in New Issue
Block a user