mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-10-10 17:38:08 +00:00
feat: Remove PERSISTENT ObjectID bit because it's not an ObjectID bit (#1881)
* feat: Remove PERSISTENT ObjectID bit because it's not an ObjectID bit TODO: Need to add character save migration for the pet subkey in the inventory Tested that the migrations work on mysql and sqlite and that properties have all their contents as before. Need to test pets still * fix: ugc, pet ids. remove persistent bit
This commit is contained in:
@@ -18,7 +18,8 @@ enum class eCharacterVersion : uint32_t {
|
||||
SPEED_BASE,
|
||||
// Fixes nexus force explorer missions
|
||||
NJ_JAYMISSIONS,
|
||||
UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
|
||||
NEXUS_FORCE_EXPLORER, // Fixes pet ids in player inventories
|
||||
UP_TO_DATE, // will become PET_IDS
|
||||
};
|
||||
|
||||
#endif //!__ECHARACTERVERSION__H__
|
||||
|
@@ -1,13 +1,12 @@
|
||||
#ifndef __EOBJECTBITS__H__
|
||||
#define __EOBJECTBITS__H__
|
||||
#ifndef EOBJECTBITS_H
|
||||
#define EOBJECTBITS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum class eObjectBits : size_t {
|
||||
PERSISTENT = 32,
|
||||
CLIENT = 46,
|
||||
SPAWNED = 58,
|
||||
CHARACTER = 60
|
||||
};
|
||||
|
||||
#endif //!__EOBJECTBITS__H__
|
||||
#endif //!EOBJECTBITS_H
|
||||
|
@@ -16,7 +16,7 @@ public:
|
||||
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
LWOOBJID ugcId{};
|
||||
std::array<LWOOBJID, 5> behaviors{};
|
||||
};
|
||||
|
||||
|
@@ -168,91 +168,91 @@ private:
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) {
|
||||
// LOG("%s", param.data());
|
||||
LOG_DEBUG("%s", param.data());
|
||||
stmt->setString(index, param.data());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) {
|
||||
// LOG("%s", param);
|
||||
LOG_DEBUG("%s", param);
|
||||
stmt->setString(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) {
|
||||
// LOG("%s", param.c_str());
|
||||
LOG_DEBUG("%s", param.c_str());
|
||||
stmt->setString(index, param.c_str());
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setByte(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setByte(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setShort(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setShort(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) {
|
||||
// LOG("%u", param);
|
||||
LOG_DEBUG("%u", param);
|
||||
stmt->setUInt(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%d", param);
|
||||
stmt->setInt(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) {
|
||||
// LOG("%llu", param);
|
||||
LOG_DEBUG("%llu", param);
|
||||
stmt->setInt64(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) {
|
||||
// LOG("%llu", param);
|
||||
LOG_DEBUG("%llu", param);
|
||||
stmt->setUInt64(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) {
|
||||
// LOG("%f", param);
|
||||
LOG_DEBUG("%f", param);
|
||||
stmt->setFloat(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) {
|
||||
// LOG("%f", param);
|
||||
LOG_DEBUG("%f", param);
|
||||
stmt->setDouble(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) {
|
||||
// LOG("%d", param);
|
||||
LOG_DEBUG("%s", param ? "true" : "false");
|
||||
stmt->setBoolean(index, param);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) {
|
||||
// LOG("Blob");
|
||||
LOG_DEBUG("Blob");
|
||||
// This is the one time you will ever see me use const_cast.
|
||||
stmt->setBlob(index, const_cast<std::istream*>(param));
|
||||
}
|
||||
@@ -260,10 +260,10 @@ inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istr
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
|
||||
if (param) {
|
||||
// LOG("%d", param.value());
|
||||
LOG_DEBUG("%d", param.value());
|
||||
stmt->setInt(index, param.value());
|
||||
} else {
|
||||
// LOG("Null");
|
||||
LOG_DEBUG("Null");
|
||||
stmt->setNull(index, sql::DataType::SQLNULL);
|
||||
}
|
||||
}
|
||||
@@ -271,10 +271,10 @@ inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::opti
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
|
||||
if (param) {
|
||||
// LOG("%d", param.value());
|
||||
LOG_DEBUG("%d", param.value());
|
||||
stmt->setInt64(index, param.value());
|
||||
} else {
|
||||
// LOG("Null");
|
||||
LOG_DEBUG("Null");
|
||||
stmt->setNull(index, sql::DataType::SQLNULL);
|
||||
}
|
||||
}
|
||||
|
@@ -336,8 +336,11 @@ void Character::WriteToDatabase() {
|
||||
tinyxml2::XMLPrinter printer(0, true, 0);
|
||||
m_Doc.Print(&printer);
|
||||
|
||||
// Update the xml on the character for future use if needed
|
||||
m_XMLData = printer.CStr();
|
||||
|
||||
//Finally, save to db:
|
||||
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
|
||||
Database::Get()->UpdateCharacterXml(m_ID, m_XMLData);
|
||||
}
|
||||
|
||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
|
@@ -360,9 +360,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
} while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants
|
||||
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT);
|
||||
|
||||
xml << "<i l=\"" << shirtLOT << "\" id=\"" << lwoidforshirt << "\" s=\"0\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
xml << "<i l=\"" << pantsLOT << "\" id=\"" << lwoidforpants << "\" s=\"1\" c=\"1\" eq=\"1\" b=\"1\"/>";
|
||||
|
@@ -402,7 +402,8 @@ public:
|
||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||
|
||||
void UpdateGroup(const GroupUpdate& groupUpdate);
|
||||
void RemoveGroup(const std::string& groupId);
|
||||
|
||||
std::unordered_map<LWOOBJID, DatabasePet>& GetPetsMut() { return m_Pets; };
|
||||
|
||||
void FixInvisibleItems();
|
||||
|
||||
|
@@ -482,7 +482,6 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
|
||||
LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID();
|
||||
|
||||
GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT);
|
||||
|
||||
m_DatabaseId = petSubKey;
|
||||
|
||||
|
@@ -622,7 +622,6 @@ void PropertyManagementComponent::Load() {
|
||||
if (databaseModel.lot == 14) {
|
||||
LWOOBJID blueprintID = databaseModel.ugcId;
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
||||
|
||||
settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
|
||||
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
|
||||
|
@@ -2587,12 +2587,10 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
|
||||
if (!entity || !entity->GetCharacter() || !entity->GetCharacter()->GetParentUser()) return;
|
||||
LWOOBJID newIDL = newID;
|
||||
GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT);
|
||||
|
||||
uint32_t blueprintIDSmall = ObjectIDManager::GenerateRandomObjectID();
|
||||
LWOOBJID blueprintID = blueprintIDSmall;
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
||||
|
||||
//We need to get the propertyID: (stolen from Wincent's propertyManagementComp)
|
||||
const auto& worldId = Game::zoneManager->GetZone()->GetZoneID();
|
||||
|
@@ -101,7 +101,6 @@ Item::Item(
|
||||
LWOOBJID id = ObjectIDManager::GenerateRandomObjectID();
|
||||
|
||||
GeneralUtils::SetBit(id, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(id, eObjectBits::PERSISTENT);
|
||||
|
||||
const auto type = static_cast<eItemType>(info->itemType);
|
||||
|
||||
|
@@ -82,6 +82,7 @@
|
||||
#include "MissionComponent.h"
|
||||
#include "SlashCommandHandler.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "Item.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
@@ -1038,21 +1039,6 @@ void HandlePacket(Packet* packet) {
|
||||
auto* characterComponent = player->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent<CharacterComponent>()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel(), c->GetPropertyCloneID());
|
||||
WorldPackets::SendServerState(packet->systemAddress);
|
||||
|
||||
const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID());
|
||||
|
||||
Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
if (respawnPoint != NiPoint3Constant::ZERO) {
|
||||
GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, QuatUtils::IDENTITY);
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructAllEntities(packet->systemAddress);
|
||||
|
||||
characterComponent->RocketUnEquip(player);
|
||||
|
||||
// Do charxml fixes here
|
||||
auto* levelComponent = player->GetComponent<LevelProgressionComponent>();
|
||||
auto* const inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
@@ -1060,6 +1046,7 @@ void HandlePacket(Packet* packet) {
|
||||
if (!levelComponent || !missionComponent || !inventoryComponent) return;
|
||||
|
||||
auto version = levelComponent->GetCharacterVersion();
|
||||
LOG("Updating character from version %s", StringifiedEnum::ToString(version).data());
|
||||
switch (version) {
|
||||
case eCharacterVersion::RELEASE:
|
||||
// TODO: Implement, super low priority
|
||||
@@ -1108,6 +1095,29 @@ void HandlePacket(Packet* packet) {
|
||||
}
|
||||
|
||||
if (complete) missionComponent->CompleteMission(937 /* Nexus Force explorer */);
|
||||
levelComponent->SetCharacterVersion(eCharacterVersion::NEXUS_FORCE_EXPLORER);
|
||||
[[fallthrough]];
|
||||
}
|
||||
case eCharacterVersion::NEXUS_FORCE_EXPLORER: {
|
||||
LOG("Fixing pet IDs");
|
||||
|
||||
// First copy the original ids
|
||||
const auto pets = inventoryComponent->GetPetsMut();
|
||||
|
||||
// Then clear the pets so we can re-add them with the updated IDs
|
||||
auto& invPets = inventoryComponent->GetPetsMut();
|
||||
invPets.clear();
|
||||
for (auto& [id, databasePet] : pets) {
|
||||
const auto originalID = id;
|
||||
const auto newId = GeneralUtils::ClearBit(id, 32); // Persistent bit that didn't exist
|
||||
LOG("New ID %llu", newId);
|
||||
auto* item = inventoryComponent->FindItemBySubKey(originalID);
|
||||
if (item) {
|
||||
LOG("item subkey %llu", item->GetSubKey());
|
||||
item->SetSubKey(newId);
|
||||
invPets[newId] = databasePet;
|
||||
}
|
||||
}
|
||||
levelComponent->SetCharacterVersion(eCharacterVersion::UP_TO_DATE);
|
||||
[[fallthrough]];
|
||||
}
|
||||
@@ -1115,6 +1125,24 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the characters xml to ensure the update above is not only saved, but so the client picks up on the changes.
|
||||
c->SaveXMLToDatabase();
|
||||
|
||||
WorldPackets::SendCreateCharacter(packet->systemAddress, characterComponent->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel(), c->GetPropertyCloneID());
|
||||
WorldPackets::SendServerState(packet->systemAddress);
|
||||
|
||||
const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID());
|
||||
|
||||
Game::entityManager->ConstructEntity(player, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
if (respawnPoint != NiPoint3Constant::ZERO) {
|
||||
GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, QuatUtils::IDENTITY);
|
||||
}
|
||||
|
||||
Game::entityManager->ConstructAllEntities(packet->systemAddress);
|
||||
|
||||
characterComponent->RocketUnEquip(player);
|
||||
|
||||
player->GetCharacter()->SetTargetScene("");
|
||||
|
||||
// Fix the destroyable component
|
||||
@@ -1149,8 +1177,6 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
//Send message:
|
||||
LWOOBJID blueprintID = bbbModel.id;
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
||||
|
||||
// Workaround for not having a UGC server to get model LXFML onto the client so it
|
||||
// can generate the physics and nif for the object.
|
||||
|
37
migrations/dlu/mysql/24_remove_persistent_bit.sql
Normal file
37
migrations/dlu/mysql/24_remove_persistent_bit.sql
Normal file
@@ -0,0 +1,37 @@
|
||||
START TRANSACTION;
|
||||
CREATE TABLE `ugc_2` (
|
||||
`id` BIGINT NOT NULL PRIMARY KEY,
|
||||
`account_id` INT(11) NOT NULL REFERENCES `accounts` (`id`),
|
||||
`character_id` BIGINT(20) NOT NULL REFERENCES `charinfo` (`id`),
|
||||
`is_optimized` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`lxfml` MEDIUMBLOB NOT NULL,
|
||||
`bake_ao` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`filename` TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE TABLE `properties_contents_2` (
|
||||
`id` BIGINT PRIMARY KEY NOT NULL,
|
||||
`property_id` BIGINT NOT NULL REFERENCES `properties`(`id`),
|
||||
`ugc_id` BIGINT DEFAULT NULL REFERENCES `ugc_2`(`id`),
|
||||
`lot` INT NOT NULL,
|
||||
`x` FLOAT NOT NULL,
|
||||
`y` FLOAT NOT NULL,
|
||||
`z` FLOAT NOT NULL,
|
||||
`rx` FLOAT NOT NULL,
|
||||
`ry` FLOAT NOT NULL,
|
||||
`rz` FLOAT NOT NULL,
|
||||
`rw` FLOAT NOT NULL,
|
||||
`model_name` TEXT NOT NULL DEFAULT '',
|
||||
`model_description` TEXT NOT NULL DEFAULT '',
|
||||
`behavior_1` BIGINT DEFAULT 0,
|
||||
`behavior_2` BIGINT DEFAULT 0,
|
||||
`behavior_3` BIGINT DEFAULT 0,
|
||||
`behavior_4` BIGINT DEFAULT 0,
|
||||
`behavior_5` BIGINT DEFAULT 0
|
||||
);
|
||||
INSERT INTO `ugc_2` SELECT `id`|0x1000000000000000,`account_id`,`character_id`,`is_optimized`,`lxfml`,`bake_ao`,`filename` FROM `ugc`;
|
||||
INSERT INTO `properties_contents_2` SELECT `id`,`property_id`,`ugc_id`|0x1000000000000000,`lot`,`x`,`y`,`z`,`rx`,`ry`,`rz`,`rw`,`model_name`,`model_description`,`behavior_1`,`behavior_2`,`behavior_3`,`behavior_4`,`behavior_5` FROM `properties_contents`;
|
||||
DROP TABLE `properties_contents`;
|
||||
DROP TABLE `ugc`;
|
||||
RENAME TABLE `properties_contents_2` TO `properties_contents`;
|
||||
RENAME TABLE `ugc_2` TO `ugc`;
|
||||
COMMIT;
|
1
migrations/dlu/mysql/25_fix_pet_ids.sql
Normal file
1
migrations/dlu/mysql/25_fix_pet_ids.sql
Normal file
@@ -0,0 +1 @@
|
||||
update pet_names set id = id % 0x100000000 | 0x1000000000000000;
|
37
migrations/dlu/sqlite/7_remove_persistent_bit.sql
Normal file
37
migrations/dlu/sqlite/7_remove_persistent_bit.sql
Normal file
@@ -0,0 +1,37 @@
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE `ugc_2` (
|
||||
`id` BIGINT NOT NULL PRIMARY KEY,
|
||||
`account_id` INT NOT NULL REFERENCES `accounts` (`id`),
|
||||
`character_id` BIGINT NOT NULL REFERENCES `charinfo` (`id`),
|
||||
`is_optimized` INT NOT NULL DEFAULT 0,
|
||||
`lxfml` BLOB NOT NULL,
|
||||
`bake_ao` INT NOT NULL DEFAULT 0,
|
||||
`filename` TEXT NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE TABLE `properties_contents_2` (
|
||||
`id` BIGINT PRIMARY KEY NOT NULL,
|
||||
`property_id` BIGINT NOT NULL REFERENCES `properties`(`id`),
|
||||
`ugc_id` BIGINT DEFAULT NULL REFERENCES `ugc_2`(`id`),
|
||||
`lot` INT NOT NULL,
|
||||
`x` DOUBLE NOT NULL,
|
||||
`y` DOUBLE NOT NULL,
|
||||
`z` DOUBLE NOT NULL,
|
||||
`rx` DOUBLE NOT NULL,
|
||||
`ry` DOUBLE NOT NULL,
|
||||
`rz` DOUBLE NOT NULL,
|
||||
`rw` DOUBLE NOT NULL,
|
||||
`model_name` TEXT NOT NULL DEFAULT '',
|
||||
`model_description` TEXT NOT NULL DEFAULT '',
|
||||
`behavior_1` BIGINT DEFAULT 0,
|
||||
`behavior_2` BIGINT DEFAULT 0,
|
||||
`behavior_3` BIGINT DEFAULT 0,
|
||||
`behavior_4` BIGINT DEFAULT 0,
|
||||
`behavior_5` BIGINT DEFAULT 0
|
||||
);
|
||||
INSERT INTO `ugc_2` SELECT `id`|0x1000000000000000,`account_id`,`character_id`,`is_optimized`,`lxfml`,`bake_ao`,`filename` FROM `ugc`;
|
||||
INSERT INTO `properties_contents_2` SELECT `id`,`property_id`,`ugc_id`|0x1000000000000000,`lot`,`x`,`y`,`z`,`rx`,`ry`,`rz`,`rw`,`model_name`,`model_description`,`behavior_1`,`behavior_2`,`behavior_3`,`behavior_4`,`behavior_5` FROM `properties_contents`;
|
||||
DROP TABLE `properties_contents`;
|
||||
DROP TABLE `ugc`;
|
||||
ALTER TABLE `properties_contents_2` RENAME TO `properties_contents`;
|
||||
ALTER TABLE `ugc_2` RENAME TO `ugc`;
|
||||
COMMIT;
|
2
migrations/dlu/sqlite/8_fix_pet_ids.sql
Normal file
2
migrations/dlu/sqlite/8_fix_pet_ids.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
/* Unset the fake persistent bit alongside the Character bit and then re-set the Character bit */
|
||||
update pet_names set id = id % 0x100000000 | 0x1000000000000000;
|
Reference in New Issue
Block a user