mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
A placed model's client (1.10.64, UGCUSE3DSERVICES=7:0) always loads its blueprint's NIF and HKX (its BlueprintComponent sets renderUserGen and physicsUserGen itself) and its LXFML, asks the world for each file's manifest first and waits for the answer with no timeout. With ugc_manifest=1 and the new ugc_manifest_models=1 (default 0) the world: - leaves the models whose mesh the UGC server made out of the LXFML it sends when a property loads, - answers their NIF with the UGC server's checksum, their LXFML with the stored LXFML's (worked out once and kept) and their HKX as not known, - sends a model that isn't made its LXFML (once for the three requests), so the client builds it itself and no model is left waiting. When the UGC server writes a model's mesh with a new checksum it sends UGC_MODELS_MADE (a new master message, appended) to the master, which passes it to every world; a world with that model placed sends its players the new NIF checksum and NotifyClientUGCModelReady (game message 909, the blueprint id), so clients switch to the served mesh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
752 lines
27 KiB
C++
752 lines
27 KiB
C++
#include "BuildingMessages.h"
|
|
|
|
#include "BitStreamUtils.h"
|
|
#include "Character.h"
|
|
#include "ClientPackets.h"
|
|
#include "CppScripts.h"
|
|
#include "DashboardNotify.h"
|
|
#include "Database.h"
|
|
#include "dConfig.h"
|
|
#include "dServer.h"
|
|
#include "dZoneManager.h"
|
|
#include "eBlueprintSaveResponseType.h"
|
|
#include "eInventoryType.h"
|
|
#include "eLootSourceType.h"
|
|
#include "eMissionTaskType.h"
|
|
#include "eRacingTaskParam.h"
|
|
#include "eReplicaComponentType.h"
|
|
#include "Entity.h"
|
|
#include "EntityInfo.h"
|
|
#include "EntityManager.h"
|
|
#include "Game.h"
|
|
#include "GeneralUtils.h"
|
|
#include "InventoryComponent.h"
|
|
#include "Item.h"
|
|
#include "Logger.h"
|
|
#include "Lxfml.h"
|
|
#include "MissionComponent.h"
|
|
#include "ObjectIDManager.h"
|
|
#include "PropertyManagementComponent.h"
|
|
#include "Sd0.h"
|
|
#include "ScriptComponent.h"
|
|
#include "BrickByBrick.h"
|
|
#include "CDClientManager.h"
|
|
#include "CDModularBuildComponentTable.h"
|
|
#include "User.h"
|
|
#include "UserManager.h"
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
namespace {
|
|
void WriteBuildModeFields(RakNet::BitStream& bitStream, bool start, int32_t distanceType, bool modePaused, int32_t modeValue, LWOOBJID playerId, const NiPoint3& startPos) {
|
|
bitStream.Write(start);
|
|
BitStreamUtils::WriteOptional(bitStream, distanceType, -1);
|
|
bitStream.Write(modePaused);
|
|
BitStreamUtils::WriteOptional(bitStream, modeValue, 1);
|
|
bitStream.Write(playerId);
|
|
BitStreamUtils::WriteOptional(bitStream, startPos, NiPoint3Constant::ZERO);
|
|
}
|
|
|
|
bool ReadBuildModeFields(RakNet::BitStream& bitStream, bool& start, int32_t& distanceType, bool& modePaused, int32_t& modeValue, LWOOBJID& playerId, NiPoint3& startPos) {
|
|
VALIDATE_READ(bitStream.Read(start));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, distanceType, -1));
|
|
VALIDATE_READ(bitStream.Read(modePaused));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, modeValue, 1));
|
|
VALIDATE_READ(bitStream.Read(playerId));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, startPos, NiPoint3Constant::ZERO));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace GameMessages {
|
|
void StartBuildingWithItem::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(firstTime);
|
|
bitStream.Write(success);
|
|
bitStream.Write(sourceBag);
|
|
bitStream.Write(sourceId);
|
|
bitStream.Write(sourceLot);
|
|
bitStream.Write(sourceType);
|
|
bitStream.Write(targetId);
|
|
bitStream.Write(targetLot);
|
|
bitStream.Write(targetPos);
|
|
bitStream.Write(targetType);
|
|
}
|
|
|
|
bool StartBuildingWithItem::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(firstTime));
|
|
VALIDATE_READ(bitStream.Read(success));
|
|
VALIDATE_READ(bitStream.Read(sourceBag));
|
|
VALIDATE_READ(bitStream.Read(sourceId));
|
|
VALIDATE_READ(bitStream.Read(sourceLot));
|
|
VALIDATE_READ(bitStream.Read(sourceType));
|
|
VALIDATE_READ(bitStream.Read(targetId));
|
|
VALIDATE_READ(bitStream.Read(targetLot));
|
|
VALIDATE_READ(bitStream.Read(targetPos));
|
|
VALIDATE_READ(bitStream.Read(targetType));
|
|
return true;
|
|
}
|
|
|
|
void StartBuildingWithItem::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
if (!entity.HasComponent(eReplicaComponentType::PROPERTY_MANAGEMENT)) {
|
|
return;
|
|
}
|
|
|
|
auto arrangedSourceType = sourceType;
|
|
if (arrangedSourceType == 1) {
|
|
arrangedSourceType = 4;
|
|
}
|
|
|
|
LOG("Handling start building with item (%i): (%d) (%d) (%i) (%llu) (%i) (%i) (%llu) (%i) (%i)", entity.GetLOT(), firstTime, success, sourceBag, sourceId, sourceLot, arrangedSourceType, targetId, targetLot, targetType);
|
|
|
|
auto* user = UserManager::Instance()->GetUser(sysAddr);
|
|
|
|
auto* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
|
|
StartArrangingWithItem arranging;
|
|
arranging.target = player->GetObjectID();
|
|
arranging.firstTime = firstTime;
|
|
arranging.buildAreaID = entity.GetObjectID();
|
|
arranging.buildStartPos = player->GetPosition();
|
|
arranging.sourceBag = sourceBag;
|
|
arranging.sourceID = sourceId;
|
|
arranging.sourceLot = sourceLot;
|
|
arranging.sourceType = arrangedSourceType;
|
|
arranging.targetID = targetId;
|
|
arranging.targetLot = targetLot;
|
|
arranging.targetPos = targetPos;
|
|
arranging.targetType = targetType;
|
|
arranging.SendToClient(sysAddr);
|
|
}
|
|
|
|
void StartArrangingWithItem::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(firstTime);
|
|
BitStreamUtils::WriteOptional(bitStream, buildAreaID, LWOOBJID_EMPTY);
|
|
bitStream.Write(buildStartPos);
|
|
bitStream.Write(sourceBag);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(sourceLot);
|
|
bitStream.Write(sourceType);
|
|
bitStream.Write(targetID);
|
|
bitStream.Write(targetLot);
|
|
bitStream.Write(targetPos);
|
|
bitStream.Write(targetType);
|
|
}
|
|
|
|
bool StartArrangingWithItem::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(firstTime));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, buildAreaID, LWOOBJID_EMPTY));
|
|
VALIDATE_READ(bitStream.Read(buildStartPos));
|
|
VALIDATE_READ(bitStream.Read(sourceBag));
|
|
VALIDATE_READ(bitStream.Read(sourceID));
|
|
VALIDATE_READ(bitStream.Read(sourceLot));
|
|
VALIDATE_READ(bitStream.Read(sourceType));
|
|
VALIDATE_READ(bitStream.Read(targetID));
|
|
VALIDATE_READ(bitStream.Read(targetLot));
|
|
VALIDATE_READ(bitStream.Read(targetPos));
|
|
VALIDATE_READ(bitStream.Read(targetType));
|
|
return true;
|
|
}
|
|
|
|
void FinishArrangingWithItem::Serialize(RakNet::BitStream& bitStream) const {
|
|
BitStreamUtils::WriteOptional(bitStream, buildAreaID, LWOOBJID_EMPTY);
|
|
bitStream.Write(newSourceBag);
|
|
bitStream.Write(newSourceID);
|
|
bitStream.Write(newSourceLot);
|
|
bitStream.Write(newSourceType);
|
|
bitStream.Write(newTargetID);
|
|
bitStream.Write(newTargetLot);
|
|
bitStream.Write(newTargetType);
|
|
bitStream.Write(newTargetPos);
|
|
bitStream.Write(oldItemBag);
|
|
bitStream.Write(oldItemID);
|
|
bitStream.Write(oldItemLot);
|
|
bitStream.Write(oldItemType);
|
|
}
|
|
|
|
bool FinishArrangingWithItem::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, buildAreaID, LWOOBJID_EMPTY));
|
|
VALIDATE_READ(bitStream.Read(newSourceBag));
|
|
VALIDATE_READ(bitStream.Read(newSourceID));
|
|
VALIDATE_READ(bitStream.Read(newSourceLot));
|
|
VALIDATE_READ(bitStream.Read(newSourceType));
|
|
VALIDATE_READ(bitStream.Read(newTargetID));
|
|
VALIDATE_READ(bitStream.Read(newTargetLot));
|
|
VALIDATE_READ(bitStream.Read(newTargetType));
|
|
VALIDATE_READ(bitStream.Read(newTargetPos));
|
|
VALIDATE_READ(bitStream.Read(oldItemBag));
|
|
VALIDATE_READ(bitStream.Read(oldItemID));
|
|
VALIDATE_READ(bitStream.Read(oldItemLot));
|
|
VALIDATE_READ(bitStream.Read(oldItemType));
|
|
return true;
|
|
}
|
|
|
|
void DoneArrangingWithItem::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(newSourceBag);
|
|
bitStream.Write(newSourceID);
|
|
bitStream.Write(newSourceLot);
|
|
bitStream.Write(newSourceType);
|
|
bitStream.Write(newTargetID);
|
|
bitStream.Write(newTargetLot);
|
|
bitStream.Write(newTargetType);
|
|
bitStream.Write(newTargetPosition);
|
|
bitStream.Write(oldItemBag);
|
|
bitStream.Write(oldItemID);
|
|
bitStream.Write(oldItemLot);
|
|
bitStream.Write(oldItemType);
|
|
}
|
|
|
|
bool DoneArrangingWithItem::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(newSourceBag));
|
|
VALIDATE_READ(bitStream.Read(newSourceID));
|
|
VALIDATE_READ(bitStream.Read(newSourceLot));
|
|
VALIDATE_READ(bitStream.Read(newSourceType));
|
|
VALIDATE_READ(bitStream.Read(newTargetID));
|
|
VALIDATE_READ(bitStream.Read(newTargetLot));
|
|
VALIDATE_READ(bitStream.Read(newTargetType));
|
|
VALIDATE_READ(bitStream.Read(newTargetPosition));
|
|
VALIDATE_READ(bitStream.Read(oldItemBag));
|
|
VALIDATE_READ(bitStream.Read(oldItemID));
|
|
VALIDATE_READ(bitStream.Read(oldItemLot));
|
|
VALIDATE_READ(bitStream.Read(oldItemType));
|
|
return true;
|
|
}
|
|
|
|
void DoneArrangingWithItem::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
// Picking something new to build or arrange (a brick for a new model, a placed model to take apart) starts
|
|
// arranging again; the build area is the subject, as for StartBuildingWithItem. Leaving (newSourceType 0) gets
|
|
// no answer, as on live.
|
|
if (PropertyManagementComponent::Instance() != nullptr && newSourceType != 0) {
|
|
StartArrangingWithItem arranging;
|
|
arranging.target = character->GetObjectID();
|
|
arranging.firstTime = false;
|
|
arranging.buildAreaID = entity.GetObjectID();
|
|
arranging.buildStartPos = character->GetPosition();
|
|
arranging.sourceBag = newSourceBag;
|
|
arranging.sourceID = newSourceID;
|
|
arranging.sourceLot = newSourceLot;
|
|
arranging.sourceType = newSourceType;
|
|
arranging.targetID = newTargetID;
|
|
arranging.targetLot = newTargetLot;
|
|
arranging.targetPos = newTargetPosition;
|
|
arranging.targetType = newTargetType;
|
|
arranging.SendToClient(character->GetSystemAddress());
|
|
}
|
|
|
|
LOG("Done Arranging");
|
|
|
|
auto* inventory = inv->GetInventory(TEMP_MODELS);
|
|
|
|
std::vector<Item*> items;
|
|
|
|
for (const auto& pair : inventory->GetItems()) {
|
|
items.push_back(pair.second);
|
|
}
|
|
|
|
for (auto* item : items) {
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false, false);
|
|
}
|
|
}
|
|
|
|
void ModularBuildFinish::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write<uint8_t>(modules.size());
|
|
for (const auto module : modules) bitStream.Write(module);
|
|
}
|
|
|
|
bool ModularBuildFinish::Deserialize(RakNet::BitStream& bitStream) {
|
|
uint8_t count{}; // 3 for rockets, 7 for cars
|
|
VALIDATE_READ(bitStream.Read(count));
|
|
modules.resize(count);
|
|
for (auto& module : modules) VALIDATE_READ(bitStream.Read(module));
|
|
return true;
|
|
}
|
|
|
|
void ModularBuildFinish::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
const auto count = modules.size(); // 3 for rockets, 7 for cars
|
|
|
|
// Ends the modular build on the client
|
|
const auto finishBuilding = [&]() {
|
|
FinishArrangingWithItem finish;
|
|
finish.target = character->GetObjectID();
|
|
finish.buildAreaID = entity.GetObjectID();
|
|
finish.SendToClient(character->GetSystemAddress());
|
|
|
|
ModularBuildEnd end; // i dont know if this does anything but DLUv2 did it
|
|
end.target = character->GetObjectID();
|
|
end.SendToClient(character->GetSystemAddress());
|
|
};
|
|
|
|
auto* temp = inv->GetInventory(TEMP_MODELS);
|
|
std::vector<LOT> modList;
|
|
// The build this many parts make (3: a rocket, 7: a car), from ModularBuildComponent
|
|
const auto build = CDClientManager::GetTable<CDModularBuildComponentTable>()->GetByNumberOfParts(static_cast<uint32_t>(count));
|
|
auto& oldPartList = character->GetVar<std::string>(u"currentModifiedBuild");
|
|
bool everyPieceSwapped = !oldPartList.empty(); // If the player didn't put a build in initially, then they should not get this achievement.
|
|
if (count >= 3 && count < 8) {
|
|
std::u16string modulesString;
|
|
|
|
for (uint32_t k = 0; k < count; k++) {
|
|
const auto mod = modules[k];
|
|
modList.push_back(mod);
|
|
auto modToStr = GeneralUtils::to_u16string(static_cast<uint32_t>(mod));
|
|
modulesString += u"1:" + (modToStr);
|
|
if (k + 1 != count) modulesString += u"+";
|
|
|
|
bool hasItem = false;
|
|
if (temp->GetLotCount(mod) > 0) {
|
|
hasItem = inv->RemoveItem(mod, 1, TEMP_MODELS);
|
|
} else {
|
|
hasItem = inv->RemoveItem(mod, 1, eInventoryType::ALL);
|
|
}
|
|
|
|
if (!hasItem) {
|
|
LOG("Player (%llu) attempted to finish a modular build without having all the required parts.", character->GetObjectID());
|
|
finishBuilding(); // kick them from modular build
|
|
return;
|
|
}
|
|
|
|
// Doing this check for 1 singular mission that needs to know when you've swapped every part out during a car modular build.
|
|
// Every build has the same root part (the car's chassis), so it is not checked
|
|
if (!build || mod != build->rootPartExampleLOT) {
|
|
if (oldPartList.find(GeneralUtils::UTF16ToWTF8(modToStr)) != std::string::npos) everyPieceSwapped = false;
|
|
|
|
}
|
|
}
|
|
|
|
LOG("Build finished");
|
|
finishBuilding(); // kick them from modular build
|
|
|
|
LwoNameValue config;
|
|
config.Insert(u"assemblyPartLOTs", modulesString);
|
|
|
|
LWOOBJID newID = ObjectIDManager::GetPersistentID();
|
|
|
|
if (build) {
|
|
inv->AddItem(build->createdLOT, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newID);
|
|
}
|
|
|
|
auto* pCharacter = character->GetCharacter();
|
|
Database::Get()->InsertUgcBuild(GeneralUtils::UTF16ToWTF8(modulesString), newID, pCharacter ? std::optional(character->GetCharacter()->GetID()) : std::nullopt);
|
|
|
|
auto* missionComponent = character->GetComponent<MissionComponent>();
|
|
|
|
if (entity.GetLOT() != 9980 || Game::server->GetZoneID() != 1200) {
|
|
if (missionComponent != nullptr) {
|
|
missionComponent->Progress(eMissionTaskType::SCRIPT, entity.GetLOT(), entity.GetObjectID());
|
|
if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, static_cast<LWOOBJID>(eRacingTaskParam::MODULAR_BUILDING));
|
|
}
|
|
}
|
|
|
|
entity.GetScript()->OnModularBuildExit(&entity, character, count >= 3, modList);
|
|
|
|
// Move remaining temp models back to models
|
|
std::vector<Item*> items;
|
|
|
|
for (const auto& pair : temp->GetItems()) {
|
|
items.push_back(pair.second);
|
|
}
|
|
|
|
for (auto* item : items) {
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ModularBuildMoveAndEquip::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(templateID);
|
|
}
|
|
|
|
bool ModularBuildMoveAndEquip::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(templateID));
|
|
return true;
|
|
}
|
|
|
|
void ModularBuildMoveAndEquip::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
|
|
LOG("Build and move");
|
|
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemByLot(templateID, TEMP_MODELS);
|
|
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
|
|
inv->MoveItemToInventory(item, eInventoryType::MODELS, 1, false, true);
|
|
}
|
|
|
|
void ModularBuildConvertModel::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(modelID);
|
|
}
|
|
|
|
bool ModularBuildConvertModel::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(modelID));
|
|
return true;
|
|
}
|
|
|
|
void ModularBuildConvertModel::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
User* user = UserManager::Instance()->GetUser(sysAddr);
|
|
if (!user) return;
|
|
Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
|
if (!character) return;
|
|
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
|
if (!inv) return;
|
|
|
|
auto* item = inv->FindItemById(modelID);
|
|
|
|
if (item == nullptr) {
|
|
return;
|
|
}
|
|
|
|
item->Disassemble(TEMP_MODELS);
|
|
|
|
Database::Get()->DeleteUgcBuild(item->GetSubKey());
|
|
|
|
item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD);
|
|
}
|
|
|
|
void SetBuildMode::Serialize(RakNet::BitStream& bitStream) const {
|
|
WriteBuildModeFields(bitStream, start, distanceType, modePaused, modeValue, playerId, startPos);
|
|
}
|
|
|
|
bool SetBuildMode::Deserialize(RakNet::BitStream& bitStream) {
|
|
return ReadBuildModeFields(bitStream, start, distanceType, modePaused, modeValue, playerId, startPos);
|
|
}
|
|
|
|
void SetBuildMode::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
auto* player = Game::entityManager->GetEntity(playerId);
|
|
|
|
auto confirmedStartPos = startPos;
|
|
if (confirmedStartPos == NiPoint3Constant::ZERO) {
|
|
confirmedStartPos = player->GetPosition();
|
|
}
|
|
|
|
player->GetCharacter()->SetBuildMode(start);
|
|
|
|
LOG("Sending build mode confirm (%i): (%d) (%i) (%d) (%i) (%llu)", entity.GetLOT(), start, distanceType, modePaused, modeValue, playerId);
|
|
|
|
SetBuildModeConfirmed confirmed;
|
|
confirmed.target = entity.GetObjectID();
|
|
confirmed.start = start;
|
|
confirmed.warnVisitors = !start; // as a live server answered: false going in, true coming out
|
|
confirmed.modePaused = modePaused;
|
|
confirmed.modeValue = modeValue;
|
|
confirmed.playerId = playerId;
|
|
confirmed.startPos = confirmedStartPos;
|
|
confirmed.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
|
|
void SetBuildModeConfirmed::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(start);
|
|
bitStream.Write(warnVisitors);
|
|
bitStream.Write(modePaused);
|
|
bitStream.Write1(); // modeValue's flag, always set
|
|
bitStream.Write(modeValue);
|
|
bitStream.Write(playerId);
|
|
bitStream.Write1(); // startPos's flag, always set
|
|
bitStream.Write(startPos);
|
|
}
|
|
|
|
bool SetBuildModeConfirmed::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(start));
|
|
VALIDATE_READ(bitStream.Read(warnVisitors));
|
|
VALIDATE_READ(bitStream.Read(modePaused));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, modeValue, 1));
|
|
VALIDATE_READ(bitStream.Read(playerId));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, startPos, NiPoint3Constant::ZERO));
|
|
return true;
|
|
}
|
|
|
|
void BuildModeSet::Serialize(RakNet::BitStream& bitStream) const {
|
|
WriteBuildModeFields(bitStream, start, distanceType, modePaused, modeValue, playerId, startPos);
|
|
}
|
|
|
|
bool BuildModeSet::Deserialize(RakNet::BitStream& bitStream) {
|
|
return ReadBuildModeFields(bitStream, start, distanceType, modePaused, modeValue, playerId, startPos);
|
|
}
|
|
|
|
void BuildModeSet::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
LOG("Set build mode to (%d) for (%llu)", start, entity.GetObjectID());
|
|
|
|
if (entity.GetCharacter()) {
|
|
entity.GetCharacter()->SetBuildMode(start);
|
|
}
|
|
}
|
|
|
|
void UnUseBBBModel::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(bHasWorldTransform);
|
|
bitStream.Write(modelID);
|
|
BitStreamUtils::WriteOptional(bitStream, worldPos, NiPoint3Constant::ZERO);
|
|
BitStreamUtils::WriteOptional(bitStream, worldRot, QuatUtils::IDENTITY);
|
|
}
|
|
|
|
bool UnUseBBBModel::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(bHasWorldTransform));
|
|
VALIDATE_READ(bitStream.Read(modelID));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, worldPos, NiPoint3Constant::ZERO));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, worldRot, QuatUtils::IDENTITY));
|
|
return true;
|
|
}
|
|
|
|
void UnUseBBBModel::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
BrickByBrick::ReturnModel(entity, modelID, bHasWorldTransform, worldPos, worldRot);
|
|
}
|
|
|
|
void BBBLoadItemRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(itemID);
|
|
}
|
|
|
|
bool BBBLoadItemRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(itemID));
|
|
return true;
|
|
}
|
|
|
|
void BBBLoadItemRequest::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
const auto movedId = BrickByBrick::LoadModel(entity, itemID);
|
|
|
|
// A live server answered with the same id: the item keeps it in MODELS_IN_BBB. Without the model the client
|
|
// shows BBB_ERROR_LOADING_BLUEPRINT and drops the load (0x00b75bb0).
|
|
ClientPackets::BlueprintLoadItemResponse response;
|
|
response.success = movedId != LWOOBJID_EMPTY;
|
|
response.itemId = itemID;
|
|
response.destItemId = movedId != LWOOBJID_EMPTY ? movedId : itemID;
|
|
response.Send(sysAddr);
|
|
}
|
|
|
|
void BBBSaveRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(localID);
|
|
bitStream.Write<uint32_t>(lxfmlDataCompressed.size());
|
|
bitStream.WriteAlignedBytes(reinterpret_cast<const unsigned char*>(lxfmlDataCompressed.data()), lxfmlDataCompressed.size());
|
|
bitStream.Write(timeTakenInMs);
|
|
}
|
|
|
|
bool BBBSaveRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(localID));
|
|
uint32_t sd0Size{};
|
|
VALIDATE_READ(bitStream.Read(sd0Size));
|
|
// For the sake of letting players make models as big as they want, the only limit is the message itself.
|
|
bitStream.AlignReadToByteBoundary();
|
|
if (static_cast<uint64_t>(sd0Size) * 8 > bitStream.GetNumberOfUnreadBits()) return false;
|
|
lxfmlDataCompressed.resize(sd0Size);
|
|
if (sd0Size != 0) VALIDATE_READ(bitStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(lxfmlDataCompressed.data()), sd0Size));
|
|
VALIDATE_READ(bitStream.Read(timeTakenInMs));
|
|
return true;
|
|
}
|
|
|
|
void BBBSaveRequest::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
BrickByBrick::Save(entity, localID, lxfmlDataCompressed);
|
|
}
|
|
|
|
void ActivateBrickMode::Serialize(RakNet::BitStream& bitStream) const {
|
|
BitStreamUtils::WriteOptional(bitStream, buildObjectID, LWOOBJID_EMPTY);
|
|
BitStreamUtils::WriteOptional(bitStream, buildType, 2);
|
|
bitStream.Write(enterBuildFromWorld);
|
|
bitStream.Write(enterFlag);
|
|
}
|
|
|
|
bool ActivateBrickMode::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, buildObjectID, LWOOBJID_EMPTY));
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, buildType, 2));
|
|
VALIDATE_READ(bitStream.Read(enterBuildFromWorld));
|
|
VALIDATE_READ(bitStream.Read(enterFlag));
|
|
return true;
|
|
}
|
|
|
|
void ActivateBrickMode::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
if (!enterFlag) BrickByBrick::EndSession(entity);
|
|
}
|
|
|
|
void SetBBBAutosave::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write<uint32_t>(lxfmlDataCompressed.size());
|
|
if (!lxfmlDataCompressed.empty()) bitStream.Write(lxfmlDataCompressed.data(), lxfmlDataCompressed.size());
|
|
}
|
|
|
|
bool SetBBBAutosave::Deserialize(RakNet::BitStream& bitStream) {
|
|
uint32_t size{};
|
|
VALIDATE_READ(bitStream.Read(size));
|
|
if (static_cast<uint64_t>(size) * 8 > bitStream.GetNumberOfUnreadBits()) return false;
|
|
lxfmlDataCompressed.resize(size);
|
|
if (size != 0) VALIDATE_READ(bitStream.Read(lxfmlDataCompressed.data(), size));
|
|
return true;
|
|
}
|
|
|
|
void SetBBBAutosave::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
BrickByBrick::Autosave(entity, lxfmlDataCompressed);
|
|
}
|
|
|
|
void RebuildBBBAutosaveMsg::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(count);
|
|
}
|
|
|
|
bool RebuildBBBAutosaveMsg::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(count));
|
|
return true;
|
|
}
|
|
|
|
void NotifyClientUGCModelReady::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(blueprintID);
|
|
}
|
|
|
|
bool NotifyClientUGCModelReady::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(blueprintID));
|
|
return true;
|
|
}
|
|
|
|
void UGObjectMetadata::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(userModelID);
|
|
bitStream.Write(blueprintID);
|
|
BitStreamUtils::WriteLengthPrefixed(bitStream, userModelName);
|
|
BitStreamUtils::WriteLengthPrefixed(bitStream, userModelDesc);
|
|
bitStream.Write(owningPlayerID);
|
|
bitStream.Write(accountID);
|
|
BitStreamUtils::WriteLengthPrefixed(bitStream, owningPlayerName);
|
|
const auto count = static_cast<uint8_t>(std::min<size_t>(userModelBehaviors.size(), UINT8_MAX));
|
|
bitStream.Write(count);
|
|
for (uint8_t i = 0; i < count; i++) bitStream.Write(userModelBehaviors[i]);
|
|
}
|
|
|
|
bool UGObjectMetadata::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(userModelID));
|
|
VALIDATE_READ(bitStream.Read(blueprintID));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, userModelName));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, userModelDesc));
|
|
VALIDATE_READ(bitStream.Read(owningPlayerID));
|
|
VALIDATE_READ(bitStream.Read(accountID));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, owningPlayerName));
|
|
uint8_t count{};
|
|
VALIDATE_READ(bitStream.Read(count));
|
|
userModelBehaviors.resize(count);
|
|
for (auto& behavior : userModelBehaviors) VALIDATE_READ(bitStream.Read(behavior));
|
|
return true;
|
|
}
|
|
|
|
void BlueprintMetadata::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(blueprintID);
|
|
bitStream.Write(blueprintCreationTimestamp);
|
|
bitStream.Write(userModelMod);
|
|
bitStream.Write(modelBoxMins);
|
|
bitStream.Write(modelBoxMaxs);
|
|
bitStream.Write(userModelOpt);
|
|
bitStream.Write(ugcIconReady);
|
|
BitStreamUtils::WriteLengthPrefixed(bitStream, brickListColonDelim);
|
|
bitStream.Write(neverFalseIfPresentInCaps);
|
|
bitStream.Write(numberOfBricks);
|
|
}
|
|
|
|
bool BlueprintMetadata::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(blueprintID));
|
|
VALIDATE_READ(bitStream.Read(blueprintCreationTimestamp));
|
|
VALIDATE_READ(bitStream.Read(userModelMod));
|
|
VALIDATE_READ(bitStream.Read(modelBoxMins));
|
|
VALIDATE_READ(bitStream.Read(modelBoxMaxs));
|
|
VALIDATE_READ(bitStream.Read(userModelOpt));
|
|
VALIDATE_READ(bitStream.Read(ugcIconReady));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed(bitStream, brickListColonDelim));
|
|
VALIDATE_READ(bitStream.Read(neverFalseIfPresentInCaps));
|
|
VALIDATE_READ(bitStream.Read(numberOfBricks));
|
|
return true;
|
|
}
|
|
|
|
void FetchModelMetadataRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(context);
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(requestorID);
|
|
bitStream.Write(ugID);
|
|
}
|
|
|
|
bool FetchModelMetadataRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(context));
|
|
VALIDATE_READ(bitStream.Read(objectID));
|
|
VALIDATE_READ(bitStream.Read(requestorID));
|
|
VALIDATE_READ(bitStream.Read(ugID));
|
|
return true;
|
|
}
|
|
|
|
void FetchModelMetadataRequest::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
|
FetchModelMetadataResponse response;
|
|
response.target = entity.GetObjectID();
|
|
response.ugID = ugID;
|
|
response.objectID = objectID;
|
|
response.requestorID = requestorID;
|
|
response.context = context;
|
|
BrickByBrick::FillModelMetadata(entity, ugID, response);
|
|
response.Send(sysAddr);
|
|
}
|
|
|
|
void FetchModelMetadataResponse::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(ugID);
|
|
bitStream.Write(objectID);
|
|
bitStream.Write(requestorID);
|
|
bitStream.Write(context);
|
|
bitStream.Write(bHasUGData);
|
|
bitStream.Write(bHasBPData);
|
|
if (bHasUGData) ugData.Serialize(bitStream);
|
|
if (bHasBPData) bpData.Serialize(bitStream);
|
|
}
|
|
|
|
bool FetchModelMetadataResponse::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(ugID));
|
|
VALIDATE_READ(bitStream.Read(objectID));
|
|
VALIDATE_READ(bitStream.Read(requestorID));
|
|
VALIDATE_READ(bitStream.Read(context));
|
|
VALIDATE_READ(bitStream.Read(bHasUGData));
|
|
VALIDATE_READ(bitStream.Read(bHasBPData));
|
|
if (bHasUGData) VALIDATE_READ(ugData.Deserialize(bitStream));
|
|
if (bHasBPData) VALIDATE_READ(bpData.Deserialize(bitStream));
|
|
return true;
|
|
}
|
|
|
|
void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(modelLot != -1);
|
|
if (modelLot != -1) bitStream.Write(modelLot);
|
|
}
|
|
|
|
void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(amount != 0.0f);
|
|
if (amount != 0.0f) bitStream.Write(amount);
|
|
bitStream.Write(position != NiPoint3Constant::ZERO);
|
|
if (position != NiPoint3Constant::ZERO) {
|
|
bitStream.Write(position.x);
|
|
bitStream.Write(position.y);
|
|
bitStream.Write(position.z);
|
|
}
|
|
}
|
|
|
|
bool SetModelToBuild::Deserialize(RakNet::BitStream& bitStream) {
|
|
return BitStreamUtils::ReadOptional(bitStream, modelLot, LOT{ -1 });
|
|
}
|
|
|
|
bool SpawnModelBricks::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, amount, 0.0f));
|
|
bool hasPosition{};
|
|
VALIDATE_READ(bitStream.Read(hasPosition));
|
|
position = NiPoint3Constant::ZERO;
|
|
if (hasPosition) {
|
|
VALIDATE_READ(bitStream.Read(position.x));
|
|
VALIDATE_READ(bitStream.Read(position.y));
|
|
VALIDATE_READ(bitStream.Read(position.z));
|
|
}
|
|
return true;
|
|
}
|
|
}
|