ModularBuildFinish hardcoded the item a finished build becomes (6416 for 3
parts, 8092 for 7) and the car chassis part (8129) that the every-part-
swapped check skips. They now come from ModularBuildComponent: createdLOT,
<numberOfParts> and the <ExamplePartLOT> of the <rootPart> module (new
CDModularBuildComponentTable). Same results with the 1.10.64 cdclient;
tests cover the xml parsing and the lookup.
Refs #691
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mapped from the 1.10.64 client and live captures (docs/BuildWorkflow.md).
Model placement (PropertyManagementComponent):
- A brick built model placed from the inventory spawned at the world origin
with no rotation and without PlaceModelResponse/PreCreate, and was saved
to properties_contents with ugc_id 0; it is now placed where the client
put it, keeps its UGID and blueprint and is saved with them.
- Picking up, putting away and taking apart a brick built model gave it to
MODELS_IN_BBB and then deleted it; every way off the property now puts it
in MODELS (carried when picked up), as live, with its blueprint config.
Taking a premade model apart no longer deletes it either.
- Placing and removing a model saves the property at once, so a crash or a
disconnect before PropertyEditorEnd does not lose it.
- DoneArrangingWithItem only answers when something new is picked (not when
leaving), with the subject as build area; SetBuildModeConfirmed's
warnVisitors matches live.
Brick by brick (BrickByBrick):
- BBBLoadItemRequest moves the model to MODELS_IN_BBB keeping its id and
fails cleanly when the player has no such model.
- MoveInventoryBatch moves bricks between BRICKS and BRICKS_IN_BBB (it was
not handled, so the client and server disagreed until a relog).
- BBBSaveRequest uses up the opened models, places the new ones through the
property, returns the bricks (or uses them with bbb_consume_bricks=1),
clears the autosave and sends RequeryPropertyModels. Every save makes new
ugc rows (is_optimized 0, so the UGC server processes them).
- Quick save: SetBBBAutosave is stored per character (bbb_autosave).
- UnUseBBBModel puts a model back on the property where it was when it came
from there, otherwise back in MODELS.
- Leaving brick mode without a save, a disconnect or a crash: the autosave
is rebuilt into models (RebuildBBBAutosaveMsg) or the opened models go
back to MODELS. MODELS_IN_BBB is saved with the character now and loads
into MODELS, BRICKS_IN_BBB into BRICKS.
Fixes#1632Fixes#159Fixes#1565
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TrackLOTCollection hardcoded the 15 life/armor/imagination power-up LOTs. A power-up
(Objects.type "Powerup") now counts towards the statistic of what its pickup skill
restores: a Heal, RepairArmor or Imagination behavior in the skill's behavior tree.
Gives the same result for the 15 LOTs; "HoT Powerup" (8208, heal over time) now also
counts as a life power-up.
Refs #691
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server hardcoded each item set's passives by set ID. They now come from data:
- On-kill bonuses (Paradox imagination, Sentinel armor repair, Bat Lord heal) are the
set's DarkInspiration skills in ItemSetSkills. The client turns those into a status
effect that casts the behavior's action when the wearer kills something of a faction
in faction_list; the server now runs the same behavior on the smashed enemy, so the
amounts, faction check and extra effects (e.g. rank 3 Sorcerer team imagination) follow
the data. A behavior repeated in a higher tier does not stack.
- Low imagination / low armor skills are what the live equipmenttriggers item scripts
do. Items link to those scripts through their ScriptComponent; the script vars (skill,
items required, set, cooldown) only exist in the scripts, so they are mirrored in a
small table keyed by script name. The Sentinel scripts have no cooldown (was 11s).
- Knockback immunity while quickbuilding comes from the Immunity behavior's
immune_quickbuild_interrupts (the 5 item Assembly rank 2/3 set skill) instead of a
set ID list; ImmunityBehavior now pops its immunities when an equip skill is uncast.
Removes eItemSetPassiveAbilityID and InventoryComponent::HasAnyPassive (unused now).
Refs #691
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eReplicaComponentType had the destroyable component's registry type (7) named
BUFF, the real buff component (98) as BUFF_REAL, and a made-up DESTROYABLE =
1000 that DestroyableComponent was stored under. Now DESTROYABLE = 7 and
BUFF = 98, as in ComponentsRegistry and the client.
Undone with it:
- Destructible stats came from whichever of the "buff" (7), quick build and
collectible registry ids was set, so the few objects without a type 7 entry
read a DestructibleComponent row with an unrelated id (the NJ dragon relics
16482-16485 via their collectible id, 125 quick build LOTs when placed with
is_smashable). The type 7 entry is used now; objects without one keep the
defaults (is_smashable objects: 1 health, smashable, factions -1 and 6;
collectibles: an empty destroyable). The client does the same
(LWODestroyableComponent::AllocateComponents / DoObjectComponentLoad).
- DestroyableComponent::Reinitialize, an unused copy of that pick order.
- WriteComponents' destroyableSerialized flags: the components are written from
a list in the client's order, and where the destroyable goes (its own place
after the buff, right before a quick build that has no registry entry for it,
or after the render component) is one function.
- The dashboard's registry 7 -> DESTROYABLE mapping; the destroyable type also
has a name there now (1000 was outside magic_enum's range).
Component types are not stored or sent as enum numbers anywhere besides the
CDClient's own values, which now match. migrations/cdserver/4 is unrelated (it
restores LOT 12916's registry rows that migration 0 overwrote) and stays.
Verified: dGameTests ReplicaComponentOrderTests serialize players, enemies,
smashables, quick builds and collectibles with and without a registry entry,
NPCs, pets, vehicles, models and an entity with every listed component with
the new code and a frozen copy of the old WriteComponents, and expect the same
bits for construction and serialization (a deliberately wrong destroyable place
fails them). Full ctest passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Volumes on the server touched far more than in the client:
- An enemy's body in the physics world was a sphere the size of its aggro
radius, so trigger and damage volumes caught enemies from far away
(Cavalry Hill enemies taking damage on spawn). It is now the enemy's
own radius and collision group from its physics component.
- Trigger volumes ignored their collision group and caught everything.
dpEntity now filters with the client's collision filter
(PeCollisionFilter, 1.10.64 0x00fb6940, group table from 0x00fcf9a0):
POI walls ignore enemies, threat clearing walls ignore players, and
so on. The aggro sensor keeps seeing only players.
- Rotated boxes were tested as the axis aligned box around them, which
for a turned wall covers a big square (the AG survival boundary).
Sphere and point tests now use the box's own axes.
Proximity monitors take an optional collision group like live's
SetProximityRadius; the AM shield generators use live's (10 finds
enemies, 1 finds players).
Fixes#1127
Refs #1971
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client only simulates knockbacks on the character it controls
(LWOControllablePhysComponent::msgKnockback); enemies and NPCs are drawn
where the server puts them, so a knockback on them did nothing and
scripts stunned them instead.
MovementAIComponent::Knockback now flies the object the way the client
flies its character: a vector longer than 5 lifts it 0.5, throws it with
that velocity under WorldConfig gravity (times its gravity scale) until
it lands on the navmesh, no sooner than 250ms later; a shorter one moves
it by the vector. Walls taller than a step stop sideways motion, the
landing is put back onto the navmesh, and pathing and the combat AI wait
until it lands (destinations set mid air are walked to afterwards).
Position and velocity go out in the normal serialization every tick.
KnockbackBehavior builds the vector like the client's Cast (strength
capped at 300, angle as elevation, relative, caster and ignore_self) and
knocks back server moved targets that aren't immune, both when the
server casts and when a client's skill hits. The blocked bit it writes
now also answers for the target's knockback immunity, so a player whose
client hasn't caught up with a Personal Fortress isn't knocked out of it.
The AM shield generators knock enemies back like live instead of
stunning them.
Fixes#257
Refs #185
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff list contraband items on a new dashboard page (item search, reason, flag or flag and remove;
contraband_manage to edit, reports_view to see). World servers check every inventory when a
character loads and every item a player receives: each find is an economy flag of the new kind
Contraband, shown with the other flags and in the character's related data. Items marked for
removal are taken away, with a character snapshot kept first so they can be given back, an audit
entry and a mail or chat message telling the player why. Staff are skipped unless
contraband_ignore_staff is off. Worlds reload the list when it changes (RELOAD_CONTRABAND, added
at the end of ePlayerAction).
Fixes#1563
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every character gets a save generation in charxml. A world bumps it when it loads the character for
play, and every save from that world only goes through while the stored generation is still the
one it loaded or last saved (and moves it on). Dashboard edits, restores and maintenance writes bump
it too. A world that lost the character to another world (a disconnect noticed late, a zone
transfer, an instance migration) or to a dashboard edit can no longer overwrite the newer data: the
save is refused, logged and audited as stale_save_refused, the world stops saving that character
and a player still connected to it is disconnected with the save failure reason so they reload.
Fixes#639
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every packet of the chat service is now an LUBitStream struct in dNet/ChatPackets.h
(docs/PacketArchitecture.md, PR 13), and nothing in the chat server or the chat side of
the world server reads or writes a packet by hand any more.
- World <-> chat: LoginSessionNotify, UnexpectedDisconnect, GMLevelUpdate, GMMute,
Announcement (GM announce), CreateTeam, TeamUpdate (TEAM_GET_STATUS to worlds),
AchievementNotify, ShowAllRequest, FindPlayerRequest.
- Client -> world -> chat (friends, ignore list, teams, general and private chat):
GetFriendsList, AddFriendRequest, AddFriendResponse, RemoveFriend, GetIgnoreList,
AddIgnore, RemoveIgnore, GeneralChatMessage, PrivateChatMessage, TeamInvite,
TeamInviteResponse, TeamLeave, TeamKick, TeamSetLeader, TeamSetLoot, TeamGetStatus.
The 77 bytes the handlers skipped are named fields now (the sender block the client
fills in); the 4 unused bytes after the player ID are kept as `unknown`.
- What the client receives. Chat service (ChatPackets::Client): GeneralChatMessage
(replaces SendChatMessage; SendSystemMessage stays as a helper built on it),
PrivateChatMessage. Client service (ClientPackets, as structs go in the file of the
ServiceType in their header): SendCannedText (replaces SendMessageFail), GetFriendsListResponse, AddFriendRequest,
AddFriendResponse, RemoveFriendResponse, UpdateFriendNotify, WhoResponse,
ShowAllResponse, Get/Add/RemoveIgnoreResponse, TeamInvite, TeamInviteInitialResponse,
and the team game messages chat writes (TeamInviteConfirm, TeamGetStatusResponse,
TeamSetLeader, TeamAddPlayer, TeamRemovePlayer, TeamSetOffWorldFlag) as TeamGameMsg
structs, since chat doesn't link dGame's NetGameMsg.
- WORLD_ROUTE_PACKET is ChatPackets::WorldRoutePacket (its own file, dNet/WorldRoutePacket.h,
since it carries packets of other services): the target and the inner packet.
ChatPacketHandler::SendRouted replaces the per-function route headers and
SendRoutedMsg.
Dispatch: dNet/PacketDispatcher.h is a dispatch map from packet ID to (struct, handler
function); packets that fail to Deserialize are logged and dropped. The chat server's
switch and the world's HandlePacketChat switch are now maps. The handlers take the
structs; their logic is unchanged. World code sends to chat with ChatServerLink::Send
(dGame) instead of writing to Game::chatServer by hand. eChatChannel,
eChatMessageResponseCode and eAddIgnoreResponse moved to dCommon/dEnums so the structs
can use them.
Verified: tests/dGameTests/dNetTests/Legacy/ChatPacketsLegacy.h is a verbatim copy of
the old senders and readers; ChatPacketsTests (25 tests) sends a grid of inputs through
both and requires identical bits, bytes and destination, checks the old readers read
what the structs write, round trips every struct, checks truncated packets are refused,
and has hand written golden packets for general chat, canned text, GM mute and a routed
team game message. Breaking one field width (UpdateFriendNotify) and the TeamAddPlayer
zone flag made the tests fail. No wire bytes changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds CommonPackets (ServiceType::COMMON): ClientVersionConfirm (the
client's handshake, with the handler that logs it and answers),
ServerVersionConfirm, DisconnectNotify and GeneralNotify (layout from the
1.10.64 client's PacketHandler_MSG_SERVER_GENERAL_NOTIFY; DLU does not send
it yet). AuthPackets::LoginRequest reads the login and keeps the old login
logic in its Handle; ClientPackets::LoginResponse (with the Stamp list)
replaces the hand written response, and AuthPackets::SendLoginResponse
fills it from the settings as before. dServer::Disconnect writes a
DisconnectNotify. AuthServer's if-chain and WorldServer's COMMON case
become CommonPackets::Handle / AuthPackets::Handle, dispatch maps that
read the struct, drop and log it if it does not deserialize, then Handle.
HandleHandshake and SendHandshake are gone.
No wire change. Verified against a frozen verbatim copy of the old
functions (tests/dGameTests/dNetTests/Legacy): the old handshake and login
handlers and the new dispatchers are fed the same packets and must send
identical bytes to the same address (same RNG seed for the session key),
over grids of versions, service types, ports, response codes, error
messages, IPs and stamp lists; plus hand computed golden bytes, round
trips, truncation checks, and a deliberate field width mutation that made
the tests fail. The only behaviour difference: truncated handshake and
login packets are now dropped instead of handled with whatever was read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch:
accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy
reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live
events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved
captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes
they need.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First step of moving hand written bitstream code to struct based
messages (see docs/PacketArchitecture.md). No wire changes.
- GameMsg is now only a server-internal event (delivered to handlers
registered with RegisterMsg); NetGameMsg is a wire message with
Send(sysAddr) (UNASSIGNED broadcasts), SendToClient(sysAddr) (one
client, never broadcasts), WritePacket, Serialize, Deserialize and
Handle. Mixing them up is now a compile error. NetGameMsgEvent<T> /
DeliverLocally carry a wire message to local handlers; loot drops,
pickup, the object debugger, GM invisibility and model RequestUse
use them. The GM invisibility message keeps its target, so its bytes
are unchanged.
- Behaviour change: GameMessageHandler logs and drops messages whose
Deserialize fails instead of handling them with default fields (the
4 messages already registered: RequestUse, RequestServerObjectInfo,
ShootingGalleryFire, PickupItem).
- LUBitStream (kept as on main) gets a virtual destructor (Mail deleted
derived packets through the base) and WritePacket, also used by
ChatPackets::SendRoutedMsg with identical bytes.
- BitStreamUtils::WriteOptional/ReadOptional for default-flag fields
and WriteLengthPrefixed/ReadLengthPrefixed for length prefixed
strings.
- Every message ID enumerator (MessageType::*, ServiceType, Mail's
wire enums) is pinned with static_asserts, so renumbering or removing
one fails to compile.
- Tests: dServerMock copies each sent packet (it kept a pointer to the
caller's destroyed BitStream); PacketTestUtils.h compares packets bit
for bit; helper tests use hand computed golden bytes and equality
with the hand written patterns they replace; compile-time checks
keep the wire/internal split in place.
- docs/PacketArchitecture.md: survey, target architecture,
conventions, verification method and migration plan.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* saving from a test works
* testing works
* Update SavingTests.cpp
* test dServer stuff
* tests
* use dummy database and add missing pure fns
* add more tests
* add more tests
* add rocket tests
* Update BuffComponent.h
* Update test_xml_data.xml
* Update SavingTests.cpp
* update
* fix: more include changes
* fix: remove dZoneManager from global include
* fix: dDatabase
* fix: dCommon
* fix: object libs
* fix: rebase
* fix: bcrypt
* wip: try simplified connector build
* fix: update dockerfile
* fix: mariadb C/C++ on apple
* feat: Move scripts to CMAKE_MODULE_PATH
* fix: dPropertyBehaviors
* fix: macos?
* fix: Dockerfile
* fix: macos?
* fix: macos?
* fix: macos?
* fix: macos?
* fix: macos?
* try: install_name_tool
* fix not building on unix
* fix include paths
* Remove code changes
Will fix in another PR.
* format pass
remove 2 more included directories.
remove commented out code
add status to messages
* comments and format
surround include directories with quotes
remove commented out code
remove debug messages
* Update CMakeLists.txt
---------
Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
Brick building as of right now does not implement the undo action properly. This commit addresses the issue with undoing button being non-functional server side and implements the GM needed for addressing further issues.
Implement GameMessage UnUseModel which is called when a model in BrickBuilding is UnUsed. Important for UGC content down the line. Final code has been tested as follows:
1. Placed a model in brick build
2. saved placed a brick
3. repeat 2 and 3 twice more for 6 total models
4. Place a new model in brick mode and then edit all 7 models into one brick model instance
5. Pressing undo returns the converted model to the inventory and properly discards the other 6 without crashing. Intended live behavior is to store this in the inventory instead however behind the scenes work is needed to implement UGC models properly.
Implement enum
Implement the BlueprintSaveResponseType enum so there are less magic numbers sent via packets.
Correct int sizes from unsigned int to uint32_t
Add deserialize test
Add a test for de-serializing a GM that is sent to the client. Assertions verify the data is in the correct order and has no extra information.
Implement GTest as a testing infrastructure.
Make windows output binaries to the build folder instead of the release type folder (potentially issue further down the line)
Add a simple unit test for DestroyableComponent