This reverts commit f6914940. The client's level editor builds a zone's objects
itself from its own files, so a world server that constructs none of them has no use.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
With -editmode 1 the world loads the zone file, terrain scene map, navmesh and
level revisions, but makes no level objects, carver blockers, spawners, spawner
networks, triggers, vanity objects or live event objects. The zone control
object stays with the zone's own template, as the client makes the same one
itself (LevelManager::LoadZoneControlObject, 0x01082660); its server script
does nothing and a race track's does not take the player into a race. Such a
world lets in only accounts at editor_gm_level.
Plumbing only: no command starts an edit mode instance yet (docs/EditMode.md).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The client takes a non-zero edit as the editor being enabled. A character whose
account GM level is at least editor_gm_level (worldconfig.ini, default 9) is
saved with edit="1"; everyone else keeps 0. A civilian never gets it, whatever
the setting. The setting is in the dashboard's settings catalog.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The script (with L_BASE_SWITCH_ACTIVATED_SERVER) asks the switches in the
platform's switchGroup to tell it when they are activated and
deactivated, and sends the platform to waypoint 1 on the one and back to
waypoint 0 on the other. The NJ monastery's ice and lightning attic
platforms (LOTs 14539 and 14376) run it.
A switch can now be asked for that (SwitchComponent::
AddActivationCallback). Where live's switch tells its group that it has
loaded, the platform here looks for its switches again each second until
it has found one.
The script was on the list of scripts left out, with a note that its
platform does not exist; it is taken off the list.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
LWOPlatformRotator::LoadConfigData (0x00c33e10) reads rotX, rotY, rotZ and
rate as floats from the config; a value the level stored as another
number type read as 0 here.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
SG_TARGET.lua, the targets' server script: at the last waypoint of its
path (onArrived with isLastPoint, which the client's PushToLua
0x00b1a910 sets for a moving platform's Arrived as for a waypoint
follower's) a target notifies the cannon, FinishedPath, and sg_cannon.lua
spawns the next one as after a hit (checkSpawn). A target set to despawn
there then dies silently after 1 s, a moving platform after 2 s.
Here ships and subs sat at the end of their path until the game ended,
and nothing came after them. The script cannot load (the targets have a
script row of their own), so the cannon does it:
- the respawn is split out of RegisterHit (CheckSpawn), and the die
callback only registers a hit when the death is not the silent one;
- a moving platform target tells through PlatformAtLastWaypoint, a
waypoint follower through a new MovementAIComponent waypoint callback.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A simple mover is a LWOPlatform like a mover: made stopped at its
waypoint (12) and, unless startPathingOnLoad is false, waiting there (9)
for the shortest wait before it leaves for its other end
(ResumeTravelling 0x00c86b90, SetWaitingTime 0x00bdc860, RunPlatform
0x00c86e10). Live showed it on the NJ counterweights, which wait for
their quick build first; one without a quick build was left waiting for
a script here.
The client only reads startPathingOnLoad (LWOMovingPlatformComponent::
LoadConfigData 0x00d0f690) and never starts a platform by itself, so the
rule is the server's, taken from what live did with movers (12 with the
setting false, else 9 and off) and from the scripts: L_NJ_SPINNER_SERVER
stops its platform when it starts up, simple movers included, and the
simple movers that wait for an event without that script all have
startPathingOnLoad 0 in the maps.
The wait now runs in the component for both, in place of the timer a
built counterweight got, and a simple mover with startPathingOnLoad
false is constructed stopped (12).
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Live serialized a mover leaving a waypoint not where the waypoint is but
past it: the GF shooting gallery subs 2 units on (speed 15), the FV
dragons 5.33 (speed 40), each along the stretch they came by, and a
dragon met 2.2 s into its 5 s wait was 88 units past its waypoint (1.219
of the next stretch). At either end of a path they left from the
waypoint itself.
It is the client's LWOPlatform at work, which the server ran too:
- SetWaitingTime (0x00bdc860) takes the object's velocities away only
when the platform has stopped or is at an end of its path, so through a
wait in between the object goes on as it came;
- the percent is how far the object is from the waypoint
(CalculatePercentTravelledToWaypoint 0x00be9050);
- UpdateVelocities (0x00bd4330) on leaving turns it the next stretch's
way from where it is, and it arrives when it passes the next waypoint
(CloseToNextWaypoint 0x00bd4140), where ProcessStateChange (0x00cce380)
puts it at the waypoint;
- a platform that has not stopped looks whether it arrived while it waits
too (HandleStoppedLogic 0x00bd16a0): the dragons were at waypoint 5
without having left 4.
The server's mover now moves by its velocity in the same way, so the
state it sends on leaving carries the place and percent live sent.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
LWOPlatformRotator on a path does not move: while it travels it turns at
the angular velocity SetAngularVelocity (0x00bfa380) works out, never
waits (IncrementWaitingTime 0x004a0390) and never arrives
(HandleStoppedLogic 0x00bdc660). The velocity is the one that turns from
no rotation to NiMatrix3::FromEulerAnglesXYZ (0x0116c3a0) of rotX, rotY
and rotZ (half turns in the level config, LoadConfigData 0x00c33e10) in
half the rate, as hkpKeyFrameUtility's
setUsingPositionOrientationPair (0x012a4050) gives it: the shortest
way, the axis times the angle over the time, none for whole turns. In
reverse it is the other way round.
The server's object now turns with it, without a serialization of its
physics (each client turns its own once the rotator is sent travelling),
and is put at its waypoint as SetupPath does. A quick build's rotator
waits for the build like a mover.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The chat server passes the master server's answer for a match on to
the world servers as it is, and a world server tells each player of
the match that the map change failed. A player moved from the
dashboard and the players of a property that loads again are told the
same, and their characters stay in the zone they are in.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The master server dropped a transfer request it started no instance
for, so the world server never heard back and the player waited. It
now answers the request with an empty address and the reason in place
of the port: no room for another instance (max_instances), a zone that
is not in the ZoneTable, or another reason. A request for a private
zone that does not exist is answered the same way.
The world server passes this on as a TRANSFER_TO_WORLD without an
address, which the client shows as a failed map change, and leaves the
character where it is. The auth server fails the login when there is
no character select to send the player to.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The messages that set a property's privacy option and name, start and
finish building, place, move and remove models and change a model's
behaviors were handled for any player on the property, and outside of
a property they used a property that is not there. They are now only
handled for the owner of the property the player is on. Only an item
from a model inventory is placed as a model.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
All 450 live saves looked at had <mnt a="0"/>. The server wrote the item
of the mount being ridden, a value live never showed and the client
would start with although nobody rides after a load.
<mnt a="0"/> is now always written, riding or not. A save that holds
another value, or one that is not a number, is still read without
failing and gets 0 at the next save.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A purchase took any count, and the coin, alternate currency and
commendation prices were worked out as 32 bit numbers that wrap for a
large count. A purchase is now for 1 to 1000 items and the prices are
64 bit numbers.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Selling paid for the count in the message while the move stopped at
what the stack held. The count now has to be between 1 and the stack's
count, the item can not be one the vendor already holds for buy back,
and the coins and alternate currency are worked out from the number of
items that left the inventory.
Buying back needs an item in the buy back inventory and a count the
stack holds. Both prices are worked out as 64 bit numbers.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
MissionDialogueOK accepted or completed the mission for whichever
object the message named as the responder. The responder now has to be
the player that sent the message, the object the dialogue belongs to
has to give or take the mission (on its own or through a random
mission's pool), a mission is only accepted when the player can accept
it and only completed when it is ready to complete. The object's
script runs after these checks.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
LWORocketLaunchComponentCommon::LoadConfigData (0x00c55150) reads the
level's targetZone, targetScene, GMLevel, propertyDefaultTargetZone,
rocketLaunchPreCondition, rocketUseLaunchPrecondition and the alternative
landing keys, and the table row replaces them only while
rocketLaunchUseDBDefaults is true, its default (0x00c12ea0). The server
used the row for every pad.
31 pads in the 1.10.64 maps turn the key off. Their zones and scenes are
their rows'; what only the level has are the launch preconditions in use
on the Venture Explorer's four pads ("237;8;127|128;6|55;107;282") and
on Avant Gardens' pads to Nimbus Station, its two properties and the
Venture Explorer. Those are now checked before a launch, as the row's are.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Every live save had <mnt a="..."/>, which
LWOMountControlComponent::LoadFromSaveData (0x00bf99c0) reads as the
character's mount item (0 on the 450 saves looked at). The server wrote
no such element.
MountControlComponent now writes it: the item of the mount being ridden,
0 on foot. The saved value is read and kept; nothing is mounted from it,
as riding does not go on over a world change. A save from before gains
<mnt a="0"/> and nothing else in it changes.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The three components were made from the registry and wrote nothing, so
the client read the next component's bits as theirs.
- LWOChestComponent::Deserialize (0x00c22010), construction only: the
player (u64) and the loot offer count (u32).
- LWOChoiceBuildComponent::Deserialize (0x00bf26d0): a flag, then the
state (u32, 0 waiting, 1 building), the built object and the user
(u64 each). Always in a construction.
- LWORollerComponent::Deserialize (0x00c15920), construction: the level's
activity_camera_pos and activity_camera_lookat_pos (3 floats each) and
the projectile skill (u32, the client starts at 1348); then the flag of
the block about its user, which is never set.
LWOGameObject::Deserialize (0x00f99690) reads the components in the order
of the object's component list, which ObjectLoader2::DoObjectComponentLoad
(0x0105ebb0) fills in the order of FillComponentOrder (0x0101f8e0): the
roller after the shooting gallery and before the moving platform, the
choice build after the moving platform and before the LUP exhibit and the
model, the chest after the zone control and before render. They are
placed against the components their objects have.
The gate rush control (103) is left out: its two registry entries have no
object, and there is no component for it.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Live kept the total before the running session in <char time> and the
session's seconds in <char stime>; both changed at a login, together with
llog (time 262486 and stime 7344 became time 269830 at the next login).
The client reads both (LWOCharacterComponent::LoadFromSaveData 0x00ca4910)
and shows their sum (SendMessage 0x00d34330, GetTimePlayed). The server
wrote everything into time and no stime.
Now time + stime is read (a missing stime is 0, so a save from before
keeps its total) and time and stime are written as live had them. A
session starts when the character is picked on the character list:
charinfo.last_login is loaded with the character, a save whose llog is
another login has its stime moved into time, and llog is written as the
session's login. Without a known login the session goes on.
GetTotalTimePlayed is still every second played. The dashboard's save
check knows stime as a number.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
LWOPropertyVendorComponent::onCheckUseRequirements (0x00c2e210) lets a
player use the property guard only when the level's
property_required_mission (LoadConfigData 0x00c2e550) is finished. The
server opened the vendor and rented the property on any request.
The guard is now not opened without the mission, and a rent request
without it is answered with PropertyRentalResponse code 9, which
onPropertyRentalResponse (0x00c56d70) shows as "you failed to rent" with
UGG_PROPRENT_Dont_have_achievement. A mission that was finished and is
being repeated counts, as in the client's list of finished missions. A
guard without the key asks for nothing.
The six property maps name missions 768, 872, 873, 874, 1293 and 1322.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
For the one construction a client gets of its own player after a seamless instance move. The client applies a
construction's frame to its player at once (LWOBasePhysComponent::Deserialize, 0x00bf5a10), and world transition
state 0 makes it fade the screen in from white (LWOCharacterComponent::PostCharacterLoaded, 0x00c71660). State 3 is
read but nothing acts on it. Nothing sets either flag yet.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
LWORocketLaunchComponentCommon does not launch a player under the row's
gmLevel (msgFireEventClientSide 0x00cb78b0) or, with useLaunchPrecondition,
one that fails the row's launchPrecondition (msgCheckUseRequirements
0x00c7d0d0). The server launched on any request. Rows: the test launcher
asks for GM level 9, the pads to Gnarled Forest and Forbidden Valley for
mission 474, Starbase 3001's for mission 956.
The alternative landing precondition pointer also starts out null for a
pad without a row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOProximityMonitorComponent::LoadDataFromTemplate (0x00c2dfb0) reads the
row's Proximities as ids of ProximityTypes rows, and SendMessage
(0x00cbb930) makes, on AddedToWorld, a proximity under the type's name and
radius (30 when missing) for each type loaded on its side.
The ids were read as two radii: every launch pad ("7,8", the client's
RocketSign and WorldLauncher types) got a rocketSmall sphere of 7 and a
rocketLarge one of 8 that nothing used. Now the row's and the type's
LoadOnServer decide; of the 40 rows only a test object's has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0) refuses a
use, after the quick build check, when the row's preconditions fail or
rail_activator_active is off, so a client does not ask. The server started
the rail on any use request. The rows ask for an equipped item (44) or a
completed mission (283 to 286).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOCharacterComponent::LoadFromSaveData (0x00ca4910) reads them, and live
saves had every one (450 of 450 characters saved at least once):
- cm, the most coins a character can hold: 9223372036854775807 on every
live save, the client's own value without it. Written when missing, an
existing one is kept, and nothing caps the coins by it.
- edit (editor level), gid and gn (guild id and name).
- mldt (when the property moderation notice was last shown): 0 when
missing, as most live saves.
- v and vd: the votes left and the day they are for (GetVotesLeft /
AdjustVotesLeft). Read back; a new day fills them up to the 3 of
WorldConfig's character_votes_per_day.
- lrx.. lrrw: the last respawn point reached, once one was reached in the
world.
- lzcs: the checksum of the zone in lzid.
Saves without them load as before. stime is not written: live kept the
session's seconds in it and the time before the session in time, which
would change what time means in existing saves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jetpack mode is not saved, and the equip skill that turns it on was cast
only when the item was put on, so a player still wearing a jetpack lost
it in the next world. Live sent SetJetPackMode (use, bypass checks, effect
167, the values of the jetpack's equip skill) shortly after the player's
own construction in 12 of 28 world loads of a player with a jetpack, and
constructed the player in jetpack mode in the other 16.
The jetpack behaviors among the equip skills of the equipped items are
run again when the player has loaded. Nothing new is saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOActivityComponent::Deserialize (0x00d002b0) reads a flag, the player
count and each player with its 10 values; a construction is the only way
a client coming into range learns who is in the activity. The block was
written only while it was waiting for the next update. Live finish line
constructions (LOT 4967) had the flag and the count in 40 of 40, one of
them with a player in it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOControllablePhysComponent::msgAddRunspeedModifier (0x00be0af0) and
msgRemoveRunSpeedModifier (0x00be0720): a modifier over 1000 or of exactly
500 counts for nothing, any slow (under 500) wins with the lowest one, and
otherwise the highest boost is the speed. Before, the last one applied won,
so a small boost cast after a big one, or a boost cast while slowed,
replaced it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The destroyable's health and threat blocks, the quick build's state and
the inventory's equipped items are each read behind a flag bit
(LWODestroyableComponent::Deserialize 0x00d2bbd0 and the others). DLU
cleared their dirty flags when it wrote a construction, which goes only
to the client the object came into range of, so a change waiting for
the next update never reached the clients already watching: a health
bar, a quick build's state, a player's new gear.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOLUPExhibitComponent::Deserialize (0x00c63790) reads a flag and the
exhibit's LOT, and that is the only way the client learns which exhibit
is shown. DLU wrote the block on a construction only while it was dirty,
so a player who came into range after the first update saw no exhibit
until the next one was picked, up to 20 seconds later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The level, forced movement, possessable, possessor and combat AI parts
are each read behind a flag bit (LWOLevelProgressionComponent::
Deserialize 0x00c63bd0 and the others). DLU cleared their dirty flags
when it wrote a construction, which goes only to the client the object
came into range of, so a change waiting for the next update never
reached the clients already watching: a level up, a mount or dismount,
a rail ride.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOCollectibleComponent::Deserialize (0x00c45ec0) reads the 16-bit id
on construction and nothing on an update. DLU wrote the id on every
serialization, so the client read 16 stray bits as the flag bits of the
components after it. The replica decoder follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOPhantomPhysicsComponent::Deserialize (0x00c34db0) reads the effect
behind a flag bit. DLU cleared the dirty flag when it wrote a
construction, which goes only to the client the volume came into range
of, so the clients already watching it never got an effect change that
was waiting for the next update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A trigger's SetPhysicsVolumeEffect is "type,amount,x,y,z,use distance,
min,max". In live phantom physics constructions:
- the direction has the amount as its length: "Push,50,0.91,-0.34,-0.22"
went out as 45.68, -17.07, -11.04. DLU multiplied the arguments as
they were, so a direction that isn't a unit vector pushed harder;
- a zero direction ("Repulse,150,0,0,0,true,25,100") is no direction;
- the distances go out when use distance is "true" ("true,150,175" as 1
and 175, "true,20,50" as 1 and 50) and not when it is "1"
("Push,40.0,0,1,0,1,20,50"). DLU never wrote them.
LWOPhantomPhysicsComponent::Deserialize (0x00c34db0) reads the two
distances as floats and keeps the least of the min and 1, and a max of at
least that.
Seven arguments no longer read an eighth.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOSimplePhysicsComponent::Deserialize (0x00be4b00) reads the velocity
and the motion state each behind its own flag bit. DLU cleared both dirty
flags when it wrote a construction, which goes only to the client the
object came into range of, so the clients already watching it never got
a change that was waiting for the next update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOControllablePhysComponent::Deserialize (0x00c45770) reads the jetpack
mode, effect, flying and bypass checks bits on construction, and
LWOControllablePhysComponent::SendMessage (0x00d2f020) keeps the same
from SetJetPackMode. DLU sent the message but never kept it, so a player
who came into range of someone flying was told they had no jetpack.
Live player constructions in jetpack mode carried effect 167, flying 0
and bypass checks 1, and a 0 after the pickup radius (the client reads
that bit into the flying flag). DLU wrote the jetpack mode there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOCharacterComponent::Deserialize (0x00ca3a40) reads the GM and PVP
block and the current activity each behind its own flag bit. DLU never
cleared the two dirty flags, so after the first quick build or pet taming
every later serialization of the player carried both blocks again. They
are cleared after an update now, as the guild block's is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only a rotater on a path runs (LWOMovingPlatformComponent::SetupPlatform 0x00ccea50), and the client reads a platform that is not at a waypoint from its path's waypoints (LWOPlatform::Deserialize 0x00c870e0), which a rotater without a path does not have. It was sent travelling (2) whenever startPathingOnLoad was not false. No object in the client's maps is a rotater.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live (the Frakjaw battle captures, LOT 16141): when its quick build completes the simple mover is serialized waiting at waypoint 0 (9) and about 0.1 s later travelling (2); it stops at its end without a serialization. That is the client's LWOPlatform, active once the quick build is complete (SendMessage 0x00bdc930), waiting the shortest wait and setting off (RunPlatform 0x00c86e10). L_CAVE_PRISON_CAGE and L_MONASTERY_BOSS_INSTANCE_SERVER never send it: they ask to be told of its PlatformAtLastWaypoint, and act then.
So the simple mover sets off when it is built, the cave prison's button goes down when the counterweight arrives instead of when it is built, and Frakjaw is hit when it arrives (0.1 s and the row's 1 s after the build) instead of on a 0.5 s timer with a counterweight that never moved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOMovingPlatformComponent::SetCurrentPath (0x00d0f570, the SetCurrentPath game message of 0x00d0f5d0): the path, its starting waypoint and direction, then the subcomponents are made anew (SetupPlatform 0x00ccea50) and the mover takes the path at that waypoint. The path is written to the clients once more, whose component sets it the same way (Deserialize 0x00c88000), and the reverse flag of the path is no longer always written false.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOPlatform moves its object with SetPosition and SetRotation ignoring the dirty flags (ProcessStateChange 0x00cce380), turned between the two waypoints' rotations, and live serialized a moving platform's mover data without its physics. The server's object stayed where it spawned, so anything that asks where a platform is (the lightning orbs' knockback of issue 1352, phantoms riding a platform) had the start of its path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The state bits were named the wrong way round: 1 is waiting and 4 is stopped (LWOPlatform::StartTravelling 0x0058b170, StopTravelling 0x0058b1d0), and the client only stops on 4 (LWOPlatform::Deserialize 0x00c870e0), so a mover stopped on its way kept moving on the client. The mover now runs as LWOPlatform::RunPlatform 0x00c86e10, ArrivedAtWaypoint 0x00c86c30, HandleWaypointArrived 0x00c17bc0 and ResumeTravelling 0x00c86b90, which gives the states of the live captures: 12 until started, 9 waiting at a waypoint (25 at an end), 2 on its way, 28 stopped at an end of a path run once. A GoToWaypoint at a waypoint waits there first with the desired waypoint, as the NJ falling tiles did, and a mover that stops is not serialized for it. A quick build's mover does not run until it is built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LWOCharacterComponent::SendMessage (0x00d34330), IsValidPVPTarget: the
target is never the player itself. LWODestroyableComponent's IsEnemy asks
it for every character. DLU called a player with PVP on an enemy of
itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone::GetScene and SimplePhysicsComponent's Get/SetAngularVelocity handlers were declared and used but their
definitions were lost where the merge kept this branch's side of the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's model behaviors (every rotation, DoDamage, OnTimer, OnStartup, MoveBackToStart, moves along the model's
local axes), raw terrain reading with scene lookups and scene tools, property model and brick by brick fixes and the
chat server's player count fix, merged onto this branch's structures:
- game messages stay structs; the angular velocity messages join them and the handler sets a message's target
- strips own their speed again and the block names, moves and rotations stay in the shared action table
(PropertyBehaviorActions, which the dashboard's behavior player reads; it now lists rotations too)
- the zone keeps reading its file through ZoneFile and takes the boundaries from it, then loads the raw terrain as main
does; the old dNavigation terrain reader is gone
- the show all response stays a struct and logs a player count mismatch as main does
- the property management component tells models who sent a chat message
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>