mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
Messages in order with directions, what the server does at each step, the inventories and how model ids change, quick save and recovery, undo, and the client functions they were mapped from (1.10.64). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
219 lines
14 KiB
Markdown
219 lines
14 KiB
Markdown
# Building on a property: model placement and brick by brick
|
|
|
|
How the 1.10.64 client goes in and out of property editing, places and removes models and builds brick by brick (BBB)
|
|
models, what it expects the server to answer, and what DLU does. Sources: the client in Ghidra (addresses below,
|
|
bookmark category `BuildWorkflow`) and 2014 live captures (only the order of messages and how ids relate are used here;
|
|
no capture data is copied). Arrows: `C->S` client to server, `S->C` server to client. Message names are the client's.
|
|
|
|
Code: `dGame/dComponents/PropertyManagementComponent` (placing and removing models), `dGame/dUtilities/BrickByBrick`
|
|
(BBB sessions, autosave, recovery), messages in `dGame/dGameMessages/BuildingMessages`, `PropertyMessages`,
|
|
`InventoryMessages`, and `ClientPackets::BlueprintSaveResponse` / `BlueprintLoadItemResponse`.
|
|
|
|
## 1. Inventories and ids
|
|
|
|
| Inventory | Holds | Notes |
|
|
|---|---|---|
|
|
| `MODELS` (5) | Model items: premade models (their own LOT) and brick built models (LOT 6662) | Where every model goes when it leaves the property |
|
|
| `MODELS_IN_BBB` (3) | The models opened in BBB (the originals) | Used up by a save; given back otherwise |
|
|
| `BRICKS` (2) | Bricks | |
|
|
| `BRICKS_IN_BBB` (9) | Bricks taken into the BBB model being built | Moved with `MoveInventoryBatch` |
|
|
| `TEMP_MODELS` (6) | Modular build parts (rocket/car) | Modular builds only |
|
|
|
|
Ids of one model through the workflow (live):
|
|
|
|
- **Model item id**: a new object id every time the model becomes an item (placing then picking up gives a new item id:
|
|
one more than the placed model's id in the capture). Opening a model in BBB keeps the item id
|
|
(`BlueprintLoadItemResponse.destItemId == itemId`).
|
|
- **UGID / property model id** (`userModelID`, the `properties_contents` id, the spawner id): premade models get a new
|
|
one each time they are placed. A brick built model keeps its UGID and blueprint across pick up / place: it is the
|
|
item's `subkey` and `userModelID` config.
|
|
- **Blueprint id** (`blueprintid`, the `ugc` row): made by a BBB save, one per model the save splits into. Every save
|
|
makes new blueprint ids (the client caches blueprints by id), so an edited model is a new blueprint; the old `ugc`
|
|
row is kept.
|
|
- **Model object id**: the runtime id of the spawned object on the property; new on every spawn.
|
|
`GetModelsOnProperty` pairs it with the UGID.
|
|
- **BBB local id**: `BBBSaveRequest.localID`, a client-made id the save response must echo (client keeps it at
|
|
`BBBManager+0x18`).
|
|
|
|
A brick built model item's config (as live sent it): `blueprintid`, `userModelID`, `userModelName`, `userModelDesc`,
|
|
`userModelHasBhvr`, `userModelBehaviors` ("id,id,id,id,id"), `userModelBehaviorSourceIDs`, `userModelOpt`,
|
|
`userModelMod`, `userModelPhysicsType` (`BrickByBrick::ModelItemConfig`).
|
|
|
|
## 2. Entering and leaving property editing
|
|
|
|
1. `C->S StartBuildingWithItem` (subject: property plaque/build area; source = the thinking hat, `sourceType` 1).
|
|
`S->C StartArrangingWithItem` (`firstTime`, `buildAreaID` = subject, player position, `sourceType` 1 answered as 4).
|
|
2. `C->S SetBuildMode(start)` to the build area. `S->C SetBuildModeConfirmed` (broadcast; `warnVisitors` false going in,
|
|
true coming out).
|
|
3. `C->S PropertyEditorBegin`. Server: property goes private, visitors are sent away, equipped items are pushed, models
|
|
pause and reset (`OnStartBuilding`). Live also sent `NotifyPropertyOfEditMode` and `PropertyBuildModeUpdate`.
|
|
4. `C->S PropertyContentsFromClient`. `S->C GetModelsOnProperty` (pairs: model object, UGID).
|
|
5. `C->S BuildModeSet(start)` (player). The client's own notification; DLU records build mode on the character.
|
|
|
|
Leaving: `C->S SetBuildMode(false)`, `PopEquippedItemsState`, `DoneArrangingWithItem` (new source empty: no answer),
|
|
`S->C SetBuildModeConfirmed(false)`, `C->S PropertyEditorEnd` (property saved, models resume, privacy restored),
|
|
`C->S BuildModeSet(false)`.
|
|
|
|
## 3. Placing and removing models
|
|
|
|
Place (the model item is carried in the hand):
|
|
|
|
| Step | Messages |
|
|
|---|---|
|
|
| Equip | `C->S EquipInventory(item)`, `C->S ZonePropertyModelEquipped` (zone control) |
|
|
| Rotate | `C->S ZonePropertyModelRotated` (zone control), client side only |
|
|
| Place | `C->S PlacePropertyModel(0)` then `C->S UpdateModelFromClient(item id, position, rotation)` |
|
|
| Answer | `S->C RemoveItemFromInventory(item)`, `S->C HandleUGCEquipPreCreateBasedOnEditMode(0, UGID)`, `S->C PlaceModelResponse(position, plaque, 14, rotation)`, `S->C GetModelsOnProperty` |
|
|
|
|
`PlaceModelResponse` echoes the rotation the client sent: every field is optional and the rotation is a w, x, y, z
|
|
quaternion (`PlaceModelResponse::Deserialize`, `0x00dc0170`). DLU used to write the 4-byte response there instead
|
|
(wire fix, commit "fix(wire): PlaceModelResponse writes the model's rotation").
|
|
|
|
Remove (`C->S DeleteModelFromClient(model object, reason)`):
|
|
|
|
| Reason | Where the model goes | Answer |
|
|
|---|---|---|
|
|
| 0 picking up | `MODELS`, equipped (carried) | `AddItemToInventoryClientSync` (new item id), `EquipInventory`, `HandleUGCEquipPostDeleteBasedOnEditMode(item, count)`, `GetModelsOnProperty`, `PlaceModelResponse(16)` |
|
|
| 1 returning to inventory | `MODELS` | `AddItemToInventoryClientSync`, `GetModelsOnProperty`, `PlaceModelResponse(16)` |
|
|
| 2 breaking apart (open in BBB) | `MODELS`; the client then sends `BBBLoadItemRequest` | as picking up, not equipped |
|
|
|
|
Putting a carried model away: `C->S UnEquipInventory` and `ZonePropertyModelRemovedWhileEquipped`; nothing to answer.
|
|
|
|
DLU saves the property after each place and remove (`PropertyManagementComponent::Save`), so a crash or disconnect
|
|
before `PropertyEditorEnd` loses nothing: the item is gone only once the model is in `properties_contents`.
|
|
|
|
## 4. Brick by brick
|
|
|
|
### 4.1 Entering
|
|
|
|
- New model: `C->S DoneArrangingWithItem(newSource = a brick in BRICKS, sourceType 2)`,
|
|
`S->C StartArrangingWithItem(firstTime false, same source)`.
|
|
- Editing a placed model: `C->S DoneArrangingWithItem(newTarget = the model object, LOT 14, targetType 4)`,
|
|
`S->C StartArrangingWithItem`, then `DeleteModelFromClient(reason 2)` (3) and `BBBLoadItemRequest`.
|
|
- `C->S ActivateBrickMode(buildObjectID = build area, buildType 2, enterBuildFromWorld false, enterFlag true)`,
|
|
`C->S BuildModeSet(start)`.
|
|
|
|
`C->S BBBLoadItemRequest(item)`: the server moves the model item from `MODELS` to `MODELS_IN_BBB` keeping its id
|
|
(`RemoveItemFromInventory`, `AddItemToInventoryClientSync`) and answers `S->C BlueprintLoadItemResponse(success, item,
|
|
destItem)`. With `success` 0 the client shows `BBB_ERROR_LOADING_BLUEPRINT` and drops the load (`0x00b75bb0`). Live also
|
|
put the model's bricks in `BRICKS_IN_BBB`; DLU does not track the bricks inside models (see 6).
|
|
|
|
Bricks: each brick taken from the backpack is `C->S MoveInventoryBatch(BRICKS -> BRICKS_IN_BBB, LOT, count)` and each
|
|
brick put back the reverse. The client has already taken them out of the source bag (`0x00ce1310`); the server moves
|
|
them without telling the client about the source and answers `S->C AddItemToInventoryClientSync` for the destination.
|
|
|
|
### 4.2 Saving
|
|
|
|
`C->S BBBSaveRequest(localID, sd0 LXFML, timeTakenInMs)`. The client sends it only from **B3Close** (the exit button,
|
|
`BBBManager::OnB3Close`, `0x00b73ad0`) when the model has bricks (with `BBB_TOO_MANY_MODELS_WARNING` first when it
|
|
splits into more than 10 models); it shows a saving bar and waits (`BBB_SAVE_WAIT_FOR_PREVIOUS` meanwhile). The server:
|
|
|
|
1. splits the LXFML into models (`Lxfml::Split`), stores each as a new `ugc` row (`is_optimized` 0: the UGC server
|
|
makes its mesh and icon);
|
|
2. settles the bricks in `BRICKS_IN_BBB` (live used them up: `RemoveItemFromInventory`; DLU gives them back unless
|
|
`bbb_consume_bricks=1`);
|
|
3. `S->C BlueprintSaveResponse(localID, reason, [blueprint id, sd0 of each model])`;
|
|
4. uses up the models in `MODELS_IN_BBB` (`RemoveItemFromInventory`);
|
|
5. places the new models on the property at their centers, saves the property, clears the autosave;
|
|
6. `S->C RequeryPropertyModels` (the client then asks `PropertyContentsFromClient`).
|
|
|
|
On the save response (`BBBManager::OnBlueprintSaveResponse`, `0x00b73a50`) the client ignores any `localId` other than
|
|
the one it is waiting for; reason 0 while in BBB clears the build and leaves brick mode; any reason ends the saving
|
|
state. Reasons with a message: 2, 3, 5, 6, 7, 8, 9, 10, 13. DLU answers 10 (`ModelGenerationFailed`) when nothing
|
|
could be made from the LXFML and 11 (`PlacementFailed`, no message, the build stays) off the player's own property.
|
|
|
|
### 4.3 Quick save (autosave)
|
|
|
|
`C->S SetBBBAutosave(sd0 LXFML)` is the client's quick save of the model being built
|
|
(`BBBManager::SaveModel`, `0x00b5be30`, sent only when the model changed since the last one):
|
|
|
|
- every 5 minutes while not placing a brick (`BBBManager::AutosaveTick`, `0x00b5bfd0`);
|
|
- when the server tells the client the player is AFK (`msgInformAFK`, `0x00be1b88`) and before the client shuts down
|
|
(`PreShutdown`, `0x00be1c79`);
|
|
- when leaving brick mode (`BBBManager::Deactivate`, `0x00b71f90`).
|
|
|
|
With nothing to keep (after a save, or an emptied model) it sends the bare sd0 header (5 bytes) to clear it. (The
|
|
client also has a `BBB_SAVE` key action, 0x55; its handler was not traced.) DLU keeps the latest one per character in `bbb_autosave`, with the
|
|
ids of the models in `MODELS_IN_BBB` at the time.
|
|
|
|
`S->C RebuildBBBAutosaveMsg(count)` tells the client the server rebuilt `count` unfinished models from an autosave; it
|
|
shows `BBB_AUTOSAVE_REBUILDING_SINGLE` / `_MULTIPLE` ("... tried to rebuild it for you. Any leftover bricks have been
|
|
returned to your Backpack", `0x00cfabd0`).
|
|
|
|
### 4.4 Leaving without a save, disconnects and crashes
|
|
|
|
Leaving: `C->S BBBResetMetadataSourceItem` (when the model is empty), `C->S SetBBBAutosave` (maybe), `C->S
|
|
ActivateBrickMode(enterFlag false)`, `C->S BuildModeSet(false)`, then the property editor's leave (2).
|
|
|
|
DLU resolves every build that ends without a save the same way (`BrickByBrick::RecoverUnfinishedBuild`), when the
|
|
player leaves brick mode (`ActivateBrickMode` with `enterFlag` false) and when a character loads into a world:
|
|
|
|
- an autosave with a model: it is rebuilt into brick built model items in `MODELS` (new `ugc` rows), the models it was
|
|
made from are used up, `RebuildBBBAutosaveMsg(count)` is sent;
|
|
- otherwise the models in `MODELS_IN_BBB` go back to `MODELS` unchanged (leaving an edit without saving never loses the
|
|
original);
|
|
- bricks in `BRICKS_IN_BBB` go back to `BRICKS`; the autosave is cleared.
|
|
|
|
`MODELS_IN_BBB` is saved with the character (it used to be left out, so a disconnect in BBB deleted the model, #1632),
|
|
and on load the BBB bags load into `MODELS` and `BRICKS`. A world or client crash therefore keeps the opened models;
|
|
the next load rebuilds the autosave, if there was one, in their place.
|
|
|
|
### 4.5 Undo
|
|
|
|
The client's undo and redo (`PropertyEditUndo` / `PropertyEditRedo`, registered only by the BBB UI in
|
|
`BBBManager::RegisterUIListeners`, `0x00b73c70`) work on the BBB session; model placement has no undo. What reaches the
|
|
server:
|
|
|
|
- Bricks taken or put back: `MoveInventoryBatch` in either direction (4.1).
|
|
- Importing a placed model into the build (import tool, `BBBImportModelTool::ImportModel`, `0x00b5fd20`): it is taken
|
|
off the property (`DeleteModelFromClient` reason 2) and opened (`BBBLoadItemRequest`). Undoing it, or a model whose
|
|
blueprint could not be loaded (`BBBManager::OnBlueprintLoaded`, `0x00b73040`), sends `C->S UnUseBBBModel(model item,
|
|
bHasWorldTransform, position, rotation)` (`BBBManager::SendUnUseBBBModel`, `0x00b6b210`). With a world transform the
|
|
model came from the property: DLU places it back there; otherwise it goes back to `MODELS`. Either way the id is
|
|
dropped from the autosave's list so a later rebuild cannot use it up. With a world transform the client waits in its
|
|
saving state; DLU ends it with `BlueprintSaveResponse(localId 0, PlacementFailed)`, which keeps the build.
|
|
- Leaving without saving (4.4): the originals come back.
|
|
|
|
## 5. Modular builds (rockets and cars)
|
|
|
|
`C->S StartBuildingWithItem` / `S->C StartArrangingWithItem`, the parts are arranged client side, `C->S
|
|
ModularBuildFinish(part LOTs)`, `C->S ModularBuildMoveAndEquip(LOT)` (the built item from `TEMP_MODELS` to `MODELS`,
|
|
equipped), `S->C FinishArrangingWithItem` / `ModularBuildEnd`, `C->S DoneArrangingWithItem`. Parts sit in
|
|
`TEMP_MODELS` while building; `DoneArrangingWithItem` moves what is left there back to `MODELS`.
|
|
`ModularBuildConvertModel` takes a build apart into `TEMP_MODELS`. Unchanged by this work.
|
|
|
|
## 6. DLU choices that differ from live
|
|
|
|
- Bricks are free: a save gives the bricks in `BRICKS_IN_BBB` back to the backpack (live used them up), unless
|
|
`bbb_consume_bricks=1`. Loading a model does not put its bricks in `BRICKS_IN_BBB`, so bricks taken off a loaded
|
|
model cannot be put in the backpack (the client shows them there until the next load).
|
|
- A model opened and then emptied is given back when leaving, rather than removed as live did (its bricks were never
|
|
given to the player).
|
|
- An edited model gets new blueprint ids; the old `ugc` row stays.
|
|
|
|
## 7. Client functions (1.10.64)
|
|
|
|
| Address | Name |
|
|
|---|---|
|
|
| `0x00b5be30` | `BBBManager::SaveModel` (autosave) |
|
|
| `0x00b5b580` | `BBBManager::SendAutosaveIfChanged` |
|
|
| `0x00b5bfd0` | `BBBManager::AutosaveTick` |
|
|
| `0x00b6cb40` | `BBBManager::SendBBBSaveRequest` |
|
|
| `0x00b6cf00` | `BBBManager::SendSaveRequestTimed` |
|
|
| `0x00b6d7b0` | `BBBManager::RequestSave` |
|
|
| `0x00b73ad0` | `BBBManager::OnB3Close` |
|
|
| `0x00b756b0` | `BBBManager::RequestExit` |
|
|
| `0x00b71f90` | `BBBManager::Deactivate` |
|
|
| `0x00b73a50` | `BBBManager::OnBlueprintSaveResponse` |
|
|
| `0x00b66850` | `BBBManager::ProcessBlueprintSaveResponse` |
|
|
| `0x00b75bb0` | `OnBlueprintLoadItemResponse` |
|
|
| `0x00b73040` | `BBBManager::OnBlueprintLoaded` |
|
|
| `0x00b6b210` | `BBBManager::SendUnUseBBBModel` |
|
|
| `0x00b5fd20` / `0x00b6b400` | `BBBImportModelTool::ImportModel` / `ReturnModel` |
|
|
| `0x00b73c70` | `BBBManager::RegisterUIListeners` |
|
|
| `0x00cfabd0` | `LWOBBBComponent_Client::msgRebuildBBBAutosaveMsg` |
|
|
| `0x00d8ecb0` | `GameMessage::ActivateBrickMode::Deserialize` |
|
|
| `0x00f2af60` | `GameMessage::SetBBBAutosave::Deserialize` |
|
|
| `0x00dc0170` | `GameMessage::PlaceModelResponse::Deserialize` |
|
|
| `0x00ce1310` | `LWOInventoryComponent_Common::msgMoveInventoryBatch` |
|