Repository navigation
release/0.50.0 - #2926
Closed
QuentinGruber wants to merge 240 commits into
Closed
release/0.50.0#2926QuentinGruber wants to merge 240 commits into
QuentinGruber wants to merge 240 commits into
Conversation
QuentinGruber
commented
Oct 2, 2026
Owner
- Fix Missing structures after server crash/restart #1467: don't let decay vacancy checks wipe re-homed freeplace structures
- Fix Missing structures after server crash/restart #1467: preserve a destroyed deck/expansion's children instead of orphaning them
- Refine Missing structures after server crash/restart #1467 preserve: cascade-destroy structures instead of using the regenerated world-simple dict
- Add Missing structures after server crash/restart #1467 end-to-end save/reload regression test
- Fix Missing structures after server crash/restart #1467 (reported cause): guard setSlot against silently overwriting an occupied slot
- Harden Missing structures after server crash/restart #1467: make construction save/load resilient to corrupt/partial data
- Harden Missing structures after server crash/restart #1467: prevent construction orphans on load and placement (H4/H5/H6)
- Harden Missing structures after server crash/restart #1467: construction save-serialization hygiene (H11/H12/H13)
- Harden Missing structures after server crash/restart #1467: add save-side orphan backstop (H14, log-only)
- Missing structures after server crash/restart #1467 (H14): make construction orphan backstop production-safe at scale
- Missing structures after server crash/restart #1467: address adversarial-review findings (log accuracy, null preservation, expansion rehome)
- Missing structures after server crash/restart #1467: don't let loot/storage freeplace items block decay cleanup of a vacant deck
- Missing structures after server crash/restart #1467: log blocked slot-overwrites + fix demolish-undo double-action
- Apply prettier formatting to Missing structures after server crash/restart #1467 changes (fix CI prettier check)
- 'publish-a-next-version'
- Update exploder.jsm.ts
- Fix craft component removal counts
- Add websocket replacement for login - zone connections
- Ensure airdrops take the longest path
- first step
- Tune military loot and add scream grenade recipe
- Add grenade scream effects and world model
- ** formatting cleanup**
- Update Recipes.ts
- Update projectileentity.ts
- Refactored airdropmanager, add bombing drops and use navmesh to spawn crates
- fix the crash / add explosion at the end
- Add player remains on death via OnRagdollStop hook
- Don't expose secret in httpServer
- Add secret to default servers
- Prioritize AI sound reactions for scream grenade
- formatting
- Fix wslzconnection test
- Fix bug with PVE damage
- Increase test timeouts
- Load navmesh if airdrops or zombies are using it
- Fix WSS TLS validation error
- Log error on websocket closure for more debugging if necessary
- Rework gasser gas cloud and charge logic
- Refactor gasser gas cloud behavior
- Revert "Refactor gasser gas cloud behavior"
- Remove env-file flag from start script
- Split gasser melee and spit attack ranges
- delete stale zone connections
- Fix docker binding to wrong zoneServer port
- Rebalance gasser attacks
- prettier
- prettier
- format
- Improve gasser NPC gas attack behavior
- Refactor gasser gas release behavior
- Increase Gasser zombie threat level
- Refactor gasser death explosion and gas spread
- Remove gasser body after death
- Only permit guts to spawn for the character that completed the ragdoll sequence
- allow gasser zombies to spawn per default
- add chanceGasserPropagation
- use an empty navmesh to reduce unit test execution time
- Add random hair models for zombies
- fix banshee call hudIndicators
- Increase gasser gas release probability
- Update ServerItemDefinitions.json
- rm eslint config
- add all crate skins in AccountItem enum
- on solomode add 100 of each crates and twin galaxies skins
- allow sound detection AK47_SKULL
- add rabbit npc
- add rabbit spawns
- rm dupe mybad
- Fix undefined entity for doors and vehicles not always spawning
- shock and gas trap
- Update trapentity.ts
- Update enums.ts
- refactor applyMovementModifier
- format
- update the last HudIndicator to use setHudIndicator
- Spatial Query Optimizations
- Observer-Based Broadcasts
- Optimizations
- Network / Packet Layer
- packet layer
- fix cof for sniper / reaper
- Update soeclient.ts
- Fix dos in SOEServer
- fix: clamp NPC turn rate while attacking instead of snapping instantly
- fix: replace unpadded toHex() with Int64String() for uint64string fields
- fix(ClientProtocol_1080): reconcile Loadout 0x8703 name and ClientUpdate 0x112f00 collision
- fix(ClientProtocol_1080): align RespawnLocations schema to the 2016 client
- fix(ClientProtocol_1080): itemSubData writes trailing block iff gate == 1
- fix(ClientProtocol_1080): append trailing uint64 to Character.UpdateTwitchInfo
- fix(ClientProtocol_1080): align Weapon.AddFireGroup/ReplaceFireGroup trailing field to the 2016 client
- fix(ClientProtocol_1080): reconciliation batch 2 — latent Vehicle/Items/UI schema corrections + itemDef dead-field docs
- fix(build): remove duplicate ConstructionChildEntity import
- fix(ci): prettier formatting + regenerate zone2016 packet interfaces
- fix(emotes): bind default emote hotkeys on spawn via Items.SetEmoteItem (0xad1f)
- fix(emotes): correct Items.SetEmoteItem to RE-confirmed 3xu32 body + 1-based slots
- fix(emotes): fill SendSelfToClient.skinItems.emotes so F-key emotes work (supersedes 0xad1f)
- fix(emotes): grant emote+NV activatable abilities (0xa105) - the real F-key enable
- fix(emotes): gate NV ability grant on equipped goggles (match /nv), auto-refresh on equip
- fix(emotes): grant emote/NV abilities on IN-RANGE loadout slots (<=43) so they activate
- fix(nv): grant night vision with abilityLineId 19 (NV's AbilitySet) so P activates
- fix(nv): grant night vision at a FREE in-range slot, revert abilityLineId=19 theory
- fix
- banshee range / block scream under loc of npc
- fix(nv): own NV ability on the goggle grant + toggle NV on inbound 0xa101
- fix(nv): exclude NV goggle from loadout grant + grant NV at a FREE slot (fixes member 0)
- docs+fix(emotes/nv): finalize intended design; simplify NV to the dinput8-patch model
- fix(nv): make night vision turn off - map InitAbility(on)/UninitAbility(off) deterministically
- fix(nv): toggle NV on both Init(0xa101) and Uninit(0xa103) so re-enable works
- chore: prettier + regen packet types/interfaces
- feat(emotes): add DoubleBird emote item (id 5376, PARAM1=4) so it can play
- feat(emotes): tag skinItems.emotes with client nameHash for dynamic emote resolution (Option B)
- feat(weapons): server-side firemode/ammo-type switching for multi-firegroup weapons
- fix(weapons): emit all firegroups in per-item weapon data so multi-firegroup weapons cycle
- fix(weapons): unload old arrow on firegroup switch so the new ammo type reloads
- chore(weapons): remove temporary crossbow-reload debug logs
- feat(weapons): per-arrow on-hit effects for crossbow flaming/explosive arrows
- tune(weapons): raise explosive-arrow construction damage (/22 -> /18)
- refactor(weapons): name firemode/firegroup packet fields from crossbow work
- fix(ClientProtocol_1080): name itemSubData discriminator subDataType
- fix(ClientProtocol_1080): name ObjectiveTargetUpdate gate hasTarget
- fix(ClientProtocol_1080): name inbound item-use sub-data gate noSubData
- use bigints for current reload counts
- fix: address review feedback on spatial query optimizations
- refactor(nv): use AbilityIds.NV_GOGGLES constant instead of raw 1111272 literal
- fix(zone): skip broadcast pack/log when there are no recipients
- fix(soe): bound ack() by the real sent range, not maxSequenceAvailable
- npc only land hit when facing the target
- rm zombie br spawn
- use raycast
- perf(resources): change-gate ResourceEvent sends to stop per-tick spam
- refactor(ClientProtocol_1080): name outgoing-RE-confirmed receive-schema fields
- fix(construction): align PlacementFinalizeRequest to client world matrix + correct placement yaw
- Hide some healthbars to players without perms
- overflowing items now gets put into mounted container
- feat(construction): harden shelter/shack contents hide + spawn-time health mask
- fix(construction): make shelter player-hide deterministic to stop visibility blinking
- fix(lint): use boolean primitive not Boolean wrapper in shouldHideHealthBar
- fix(group): set invite-state byte so the client sends Group.Join natively
- fix(group): send non-sentinel invite id + zero raid-error so Group.Join accepts
- diag(group): instrument Group.Join receive path + accept low-byte joinState
- fix(group): send inviter characterId as invite unknownQword1 so client sends Group.Join
- refactor(group): apply RE-confirmed group packet/field names and drop join debug logging
- refactor(group): apply RE-confirmed Group.Roster field names
- enums
- fix(combat): correct hit-report offset, dead code, typo, and redundant assign
- fix(inventory): type ContainerMoveItem handler with its packet schema
- refactor(vehicle): apply RE-confirmed Mount response field names
- fix(shelter): re-eval shelter hide gate on group leave and disconnect
- fix(character): ship remoteWeaponExtra on fullPc and label outer resource id
- fix(abilities): guard effect add, fix effect-tag splice and melee target id, rename 0xA105 fields
- fix(survival): range-gate crate interaction, fix plant typos/double-grant, drop stale todo
- fix(weather): rename skyData rainMinStrength so the setter value reaches the wire
- refactor(position): adopt RE names for position-update 0x100/0x1000 fields
- refactor(pc): name lightweight-pc initialCharacterState and document spawn-init constants
- refactor(pc): name lightweight-pc profileId, pinning the placeholder value to preserve the wire
- fix(shelter): re-eval shelter hide gate on group join
- refactor(pc): rename remoteWeaponsExtra array field to plural for consistency
- fix(shelter): reconcile viewer set on every re-eval for already-hidden players
- fix(shelter): respect sweep eligibility in re-eval (target guards, isHidden set-once, spectator bypass)
- fix(position): add missing offset advance in posAndRot (0x1000) parse so all 8 values read distinctly
- add AI_POLICY.md
- Add nav obstacles for ground tamper and campfire
- Update ServerWeaponDefinitions.json
- re enable small shack
- update prettier
- updt deps
- Disable glow on doors & update hashes
- Implement zombie spawning on airdrop landing
- add AI_POLICY.md
- Apply suggestion from @QuentinGruber
- Update src/servers/ZoneServer2016/managers/airdropmanager.ts
- fix existing issues
- add zombie on landing setting editable via config
- Fix trap detonation for NPC targets
- 0.50.0
…uctures Demolishing or decaying a lower shelter re-homes its surviving children (upper shelter, gate, loot) into the parent foundation's freeplaceEntities. The decay vacant-foundation cleanup and the grief-foundation cleanup decided vacancy from the slot maps only, ignoring freeplaceEntities and occupiedRampSlots, so a deck still holding those re-homed structures read as empty and was destroyed after the vacancy timer -- deleting standing shelters/gates and their loot in memory, which then never persisted to the next save (the cause of the recurring 'missing structures after restart' reports). Require freeplaceEntities and ramps to be empty at all three vacancy gates (grief check, expansion-empty test, foundation vacancy gate). Adds regression tests covering survival of re-homed freeplace structures on both the vacant and grief paths, plus a guard that a genuinely empty foundation is still removed.
…orphaning them ConstructionParentEntity.destroy() only re-homed freeplace entities for SHACK types; destroying any other populated parent (e.g. an admin removing a deck/expansion with the weapon remover) left its shelters, gates and loot live in the world dictionaries but unreachable from any saved foundation, so they were dropped on the next save -- the same orphaning class as the decay bug. It now re-homes every child (slotted + freeplace) onto the surviving parent foundation when one exists (e.g. an expansion removed from a live deck), and otherwise promotes loot to world-owned (persisted) and simple structures to world-simple; sub-expansions are destroyed so their own contents are preserved too. ConstructionChildEntity.destroy() now promotes survivors to world-owned when the parent chain is already gone (broken-chain cascade) instead of silently dropping them. Known limitation: _worldSimpleConstruction is not persisted, so simple structures left on a fully-removed top deck still won't survive a restart (full fix needs a save-format change). Adds regression tests for the re-home, world-promotion and broken-chain cases.
…e regenerated world-simple dict _worldSimpleConstruction holds server-generated breakable world props (e.g. workbenches spawned from Z1_taskProps at boot) and is intentionally NOT persisted -- it is regenerated fresh each restart, so persisting it would duplicate world props. Promoting player structures into it (the prior approach, inherited from the SHACK path) neither preserves them nor belongs there. When a whole deck is removed (no surviving parent) structural pieces are now cascade-destroyed while any nested loot is preserved as world-owned (_worldLootableConstruction, which IS persisted). The expansion-removed case still re-homes children onto the surviving deck and persists. Moved the recursive cascade into a module-level cascadeDestroyConstructionChild() in constructionchildentity.ts (imported by constructionparententity) to avoid a circular-import crash: referencing ConstructionParentEntity as a value from constructionchildentity activated a load cycle (ConstructionParentEntity extends ConstructionChildEntity, undefined at module load). Regression tests updated to assert cascade + nested-loot preservation.
Validates the literal bug report: a shelter re-homed onto a foundation's freeplaceEntities (with loot stored inside it) survives the decay vacancy tick AND a serialize -> JSON -> reconstruct round-trip (getConstructionParentSaveData -> JSON.parse(JSON.stringify) -> loadConstructionParentEntity) -- i.e. what a server restart actually exercises. Fails without the Fix 1 decay change (the deck is wiped before the round-trip) and passes with it.
…g an occupied slot Two upper shelters resolving to the same slot on one lower shelter would overwrite each other in occupiedShelterSlots (setSlot wrote unconditionally), orphaning the first upper -- and the loot nested inside it -- so it was unreachable from the foundation slot graph and silently dropped from the next world save, vanishing invisibly on restart while the rest of the base remained. This matches the operator's reported symptom (an upper shelter + its contents gone after a restart), which the earlier decay/destroy commits on this branch do NOT address. setSlot now rejects a different entity on an already-occupied slot (returns false), with a self-id check that keeps reloading the same save idempotent. Placement already gates on occupancy; on load the existing freeplace fallback re-homes the rejected entity instead of dropping it. The guard is in the shared setSlot, so it protects wall/shelter/ramp/expansion slots alike. Adds a red-green regression test.
…l data
LOAD (H1): every Object.values(slotMap) in loadConstructionChildSlots/loadConstructionParentEntity is now guarded with ?? {} and each child loads inside its own try/catch. Previously a foundation save doc missing a slot map (schema drift / older-schema / partial crash-time write) made Object.values(undefined) throw; the per-foundation catch in loadConstructionParentEntities then silently discarded the ENTIRE foundation (every gate/shelter/loot under it), after which deleteMany erased its doc permanently. Now a corrupt child degrades only itself and the base + siblings survive. This is the cleanest match to the reported 'whole base gone after a crash/restart'.
SAVE (H2): each foundation and world-lootable is serialized inside its own try/catch in saveWorld; a throwing foundation is skipped instead of aborting the entire world save (which rolled back and then skipped the final save, losing every base's recent changes). Skipped ids are retained and unioned into deleteMany's $nin keep-set (saveConstructionData / saveWorldFreeplaceConstruction) so a transient serialization error does not permanently delete that foundation's last-good doc.
SHUTDOWN (H3): always attempt one best-effort final save even after a failed periodic save (worldSaveFailed suppresses retry storms, not the final save); a failed periodic save no longer re-enters shutdown when one is already in progress. Adds a load-resilience regression test.
…H5/H6) H4 (expansion load): only force-insert an expansion into a genuinely EMPTY slot (never clobber an already-registered one into an orphan); when the slot is unusable (0/garbled) or occupied, re-home the expansion's children onto the foundation (new WorldDataManager.rehomeChildrenToFoundation) so its shelters/gates/loot stay reachable from the save graph instead of being dropped with the slot-less shell. H5 (ramp load): the ramp/stairs load loop was the only slot loop that discarded setRampSlot's result with no fallback; it now re-homes a rejected ramp to freeplace like the wall/shelter loops. H6 (placement): placeConstructionWall / placeConstructionRamp / placeConstructionStairs / the expansion branch of placeConstructionFoundation now check the set*Slot() result and bail (placementError + return false) instead of inserting the entity into _constructionSimple/_constructionFoundations when the slot was rejected — mirroring placeConstructionShelter/Door and preventing a guard-rejected entity from becoming a live-but-unsaveable orphan. Adds an expansion-re-home regression test.
H11: getLootableConstructionSaveData now serializes the container via getLoadoutContainerSaveData (the canonical primitive-only shape characters/vehicles use, and this field's declared type) instead of embedding the live LoadoutContainer — whose runtime fields (e.g. a stored weapon's reloadTimer) could make the save object non-serializable and abort the entire world save. H12: removeUntransferableFields is now cycle-safe (WeakSet visited-set), so a circular reference can no longer recurse until the stack overflows synchronously inside the save build (which would abort the save). H13: saveWorldFreeplaceConstruction skips the mongo deleteMany($nin:[]) on a transient-empty snapshot so it can't wipe ALL world-lootable (including re-homed player loot); solo still does a full overwrite that correctly reflects an empty world.
After building the construction save data, saveWorld now collects every characterId reachable from the save graph (foundations recursed through all slot maps + freeplace + expansions, plus world-lootable) and logs any construction entity that is live in the world dictionaries but UNREACHABLE — i.e. an orphan that would be silently dropped on the next save. Log-only: it converts any FUTURE orphan-introducing regression into a loud, actionable warning instead of silent shelter/gate loss, without the risk of an auto-re-home. Skipped on a partial save (a foundation failed to serialize and was retained, so its children are legitimately absent this pass). O(n) single pass over data already iterated; cold interval-driven path. Adds a backstop regression test (collectReachableConstructionIds + findSaveGraphOrphans).
The orphan backstop added in 7155501 ran as two synchronous O(N) passes on every save, which on a 150-player / ~50k-entity world is a ~20-60ms event-loop stall. Make it production-safe without weakening detection: - Operator gate + sampling: a new construction.constructionOrphanCheck config flag (default true) decides whether to run, and it only runs on every Nth save (_orphanCheckSaveInterval) so most saves pay nothing. - Fold the reachable-id collection into saveWorld's existing (already yielding, every 100) foundation/world-construction build loops via the new addReachableConstructionIds helper, eliminating the separate synchronous reachable-graph walk. - findSaveGraphOrphans is now async and yields every 2000 scanned entries so the dictionary scan never blocks the tick in one burst. collectReachableConstructionIds is retained (now delegating to addReachableConstructionIds) for the orphan-backstop test. Config flag wired through defaultconfig.yaml, models/config.ts, configmanager.ts, and ConstructionManager following the existing construction-config pattern.
…vation, expansion rehome) A multi-agent adversarial review of the branch confirmed no critical/high/medium issues; these are the low-severity items worth correcting: - H14 orphan-backstop log: the message claimed flagged entities "will be dropped on this save", but the reachable set is a per-foundation snapshot, so an entity placed mid-save can appear transiently while still being reachable via its live parent (it persists on the next save). Reworded to describe snapshot semantics, and capped the listed orphans (first 50 + "...and N more") so a mass-orphan regression cannot build a multi-megabyte log line synchronously on the save path. - H14 comments: corrected two comments that described the reachable-id collection as costing "no extra synchronous pass" - it is an additive recursive walk, hoisted into the (already-yielding) build loops so it shares their yield cadence, not eliminated. - removeUntransferableFields (H12): the added value-is-not-null check incidentally routed null-valued fields into the delete branch (plus "Invalid value type" log spam), whereas they were previously preserved. Restored prior behavior with an explicit null short-circuit; the cycle guard is unchanged. - rehomeChildrenToFoundation (H4): it omitted occupiedExpansionSlots, so a child-parent nesting a sub-expansion (only reachable from corrupt slot data) would orphan that subtree under the deleted shell. Drain expansions recursively with a cycle-guarding seen-set.
…a vacant deck Fix 1 (c81eba6) made the three decay vacancy/grief gates treat a deck as non-vacant whenever freeplaceEntities was non-empty, to stop decay wiping re-homed structures (upper shelters / gates left after a supporting piece is removed). That was too broad: freeplaceEntities also holds plain loot/storage containers, so a single storage box made an otherwise-empty deck permanently immune to vacant-foundation decay and grief cleanup. That is a real regression - a storage container must not keep an abandoned deck alive. Narrow the three gates with a hasStructuralFreeplace() helper that counts only re-homed STRUCTURES (ConstructionChildEntity / ConstructionDoor), not loot (LootableConstructionEntity, which lives on a separate class branch). Now: - a deck holding a re-homed shelter/gate is still spared (Fix 1's real intent); - a deck holding only a loot/storage container decays and despawns with it. Note: #1467's actual reported failure (a single shelter + its loot vanishing from an otherwise-intact base on restart) is the setSlot overwrite-orphan, fixed separately in 4248fef; this decay path is a distinct, latent concern. Added a regression test (a lone loot container does not keep a vacant deck alive); the existing structure-protection tests already use ConstructionChildEntity and remain green.
Two follow-ups from the runtime-detach investigation: 1. setSlot diagnostic (constructionchildentity.ts): when the overwrite guard rejects a different entity from an occupied slot, log the parent, slot, and both entities at error level. This rejection is the exact moment the slot-overwrite orphan (an upper shelter + its loot silently dropped on the next save) WOULD have happened on the pre-fix unguarded code. The investigation proved the loss but could not name the in-game trigger that creates the colliding placement (naive double-placement is OVERLAP-gated). Once deployed, this log captures the real trigger from server logs without reproducing it, and confirms the fix is catching it in the field. It only fires when an overwrite is actually prevented, so it is silent on a healthy server. 2. demolishConstructionEntity (constructionmanager.ts): the undo-placement branch destroyed the entity but had no return, so it fell through and called entity.damage() again on the now-destroyed/detached entity (a double-action). Return after destroy; weapon-item wear is preserved. Unrelated to #1467; a latent wart surfaced by the same investigation.
Runs prettier --write over the five files flagged by `prettier --check ./src`. Formatting only; no logic changes. The two test files show large diffs because prettier wraps the long top-level test(...) signature, which re-indents the whole test body one level deeper — pure re-indentation, the assertions are unchanged. Verified: prettier --check passes on all five files; tsc --noEmit clean.
In craftmanager.ts:618-634, when removing craft components, the code was storing the wrong amount in the removedItems restoration array. The Fix I saved the original stack count BEFORE calling removeCraftComponent(), then used that original value when storing in the removedItems array. This ensures that if crafting fails and items need to be restored, the correct amount is restored. The Problem: When removeCraftComponent() removes items from a container, it immediately reduces the item's stackCount. The buggy code then stored this modified (reduced) stack count instead of the actual amount removed.
Adjusts military base loot tables for item 3040 by lowering its weight and tightening stack counts (min 1, max 2) across Motor Pool and Tents, and adds the same drop entry to the Hangar table. Also adds recipe 1148 to craft `GRENADE_SCREAM` from `GRENADE_SONIC_BROKEN` and `VIAL_H1Z1_B_PLASMA`.
Add WORLD_MODEL_ID to item definition and implement grenade scream item functionality with world composite effects and sound propagation. Also fixes code indentation and formatting throughout the file.
Remove exploder buddy
…ation Fix/pc position reconciliation
Cleanup/reconstruction sweep
update prettier
re enable small shack
Added functionality to spawn zombies upon airdrop landing.
Co-authored-by: Quentin Gruber <47059878+QuentinGruber@users.noreply.github.com>
Co-authored-by: Quentin Gruber <47059878+QuentinGruber@users.noreply.github.com>
Implement zombie spawning on airdrop landing
fix/zombieAirdropCrossFire
Disable glow on doors & update hashes
Fix trap detonation for NPC targets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.