Repository navigation
feat(world): let a game stop a zone that still holds entities - #576
Merged
Merged
Conversation
🟡 Code Coverage — 76.8%9486 of 12355 lines covered. 🟡 ELP Lint — 192 warnings212 diagnostics found. See job logs for details. ✅ ELP eqWAlizeNo diagnostics. |
A zone could only be stopped when its entity map was empty, so the snapshot that exists to preserve a populated zone never had anything to preserve. The only lever a script had was to empty its own entity map so the reaper would take it - which costs exactly the thing the snapshot protects. Deleting the entities was the precondition for the teardown that would have persisted them. Two ways to stop a populated zone, both of them the game module's own decision rather than the manager's idle stamp: - asobi_zone:park/1, bound as game.zone.park(), for a script that knows this zone is done; - zone_park_on_idle, which lets the idle reaper take a zone that still holds entities, so zone_idle_timeout means something for a world whose zones contain scenery. Neither takes a zone out from under players: a zone with subscribers declines and logs, and a script-busy zone still declines an idle reap. The zone-side restore is the other half. maybe_restore_from_snapshot/1 loaded zone_state, spawner, timers and tick but never entities - it never had to, because the only zone that could stop held none. It does now, and whatever a zone starts life holding is indexed into the spatial grid before its first tick, since sync_spatial_grid/3 is a diff and would never add scenery that has not moved. For a non-persistent world the zone hands its dumped zone_state to the manager on the way out and the next zone at those coords starts from it. The set is capped at max_active_zones, because a 2000x2000 grid has more coords than a node has memory. Closes #573
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.
Closes #573.
handle_cast(reap, ...)accepted only onmap_size(Entities) =:= 0, and the snapshot machinery wrote everything a zone held. The two halves never met: the only zone allowed to stop was the one whose snapshot had nothing in it.Who is asking
The manager's
reapis a proposal from an idle stamp that can lag real occupancy, so a populated zone declines it. That refusal is correct and stays. What was missing is a way for the game module to say "this zone is done", which is a different question:asobi_zone:park/1, bound asgame.zone.park()in the zone VM. A cast, like everygame.zone.*call, because it runs inside the zone's own tick.zone_park_on_idle = true, which lets the idle reaper take a zone that still holds entities - sozone_idle_timeoutmeans something for a world whose zones contain scenery, instead of being dead for any zone that has ever been visited.Neither can take a zone out from under players: a zone with subscribers declines a park and logs it (rate-limited), and a
script_busyzone still declines an idle reap, so a wave spawner is not reaped mid-countdown.The restore was only half there
maybe_restore_from_snapshot/1loadedzone_state,spawner,entity_timersandtick- but notentities. It never had to: the only zone that could stop held none, and the world server'srestore_entities/4covered the world-restart case. With a populated zone now able to stop, the zone that next occupies those coords has to bring them back, or the snapshot is written and never read. The ETS crash backup still wins where it has anything, since it is fresher.Whatever a zone starts life holding is also indexed into the spatial grid before its first tick.
sync_spatial_grid/3is a diff against the previous tick, so an entity that was there at start and never moves was never added - restored scenery would have been invisible to everygame.spatialquery for as long as it stayed still. That applied to the existing ETS-recovery path too.Non-persistent worlds
The zone hands its dumped
zone_stateto the manager before stopping and the next zone at those coords starts from it - the "somewhere to leave what was true here" that games otherwise build agen_server+ named ETS extension for. Only for non-persistent worlds: a persistent one has the whole zone in its snapshot, and a stashedzone_statewould actively suppress the snapshot load, since that path only runs for a blankzone_state.Sent before the stop, not from
terminate/2, so it is ordered ahead of the manager'sDOWNfor the same zone. Capped atmax_active_zonesentries, because a 2000x2000 grid has four million coords; past the cap the zone comes back blank and asobi logsparked_zone_state_dropped.Docs
guides/large-worlds.mdhad a "Zone state is not snapshotted on the way out / no configuration path reaches it" paragraph that stopped being true when #543 wiredpersistencethrough. It is replaced with what actually survives a park, perpersistent.Tests
asobi_zone_park_tests(park accepted where reap is refused, declined with subscribers,park_on_idlefor reap / subscribers / busy, snapshot-and-restore of a populated zone, the non-persistent stash, and no stash when persistent), plus manager tests for the parked set seeding / being consumed / respecting its cap, and twoasobi_lua_api_testsfor the binding.Checks
fmt --check,xref,dialyzer,elp eqwalize-all(156 errors, one fewer thanorigin/main's 157),elp lint,ex_doc, and the fulleunitsuite (2421 tests, 0 failures).