feat(portal): switch servers by walking or flying through portals - #326
Merged
Merged
Conversation
GsonFileHandler.load (aves 1.16.x) catches only IOException; a JsonParseException of a type adapter escapes instead of yielding Optional.empty(). MapProvider can therefore keep using the handler and abort the start on an unreadable portal (design D6, task 2.1). Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
LobbyMap gets a null-safe portals() accessor and Builder.portals(...); every copy path keeps them. PortalGsonAdapter reads and writes the box/disc contract, MapProvider validates through PortalValidator and aborts the start naming world, portal id and reason. Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
Index portals by chunk column and fire on entry only, with a 3 s cooldown per player and a permission check that never starts a cooldown. Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
PortalModule listens to PlayerMoveEvent at priority 900 and delivers via Deliver; disconnects clear the per-player state. Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
…ants Stub the mocked MapProvider with an active lobby because the portal column reads its portals at start. Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
Messages read 'portal #<index>' instead of 'portal null', as design D6 asks. Claude-Session: https://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH
Contributor
Test results 303 files 303 suites 18m 9s ⏱️ Results for commit caa9c21. |
This was referenced Sep 29, 2026
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.
Summary
Players can now switch servers by walking or flying (elytra) through a portal. Portals are map data: a
portalslist in each world'smap.json, so season worlds carry their own. Two shapes are supported:box: axis-aligned, block-inclusivemin/maxdisc:center,radius,normal, any orientation (e.g. the big leaf ring)The target is a CloudNet task. Each portal can have an optional permission.
How
core:PortalShape(Box,Disc),Portal,PortalValidator,LobbyPortals.RingPass, the box uses the slab method. A fast elytra step through a thin ring is therefore caught.LobbyMap.portalsplusPortalGsonAdapterread and write the data. Invalid portals abort startup and name the world, the portal and the reason.LobbyPortalsbean is provided byruntime.features/portal(priority 900) in both the cloudnet and local variants.Why this shape
This supersedes #222, which used an operator
portals.json, Coris cuboids only, andFeatureGate/ bridgeServiceAvailabilityfrom the unmerged #216. Portals belong to the world, so they live with the map. There is no reachability pre-check: CloudNet's task routing handles it, like the navigator.Known limits
Follow-up
Setup commands (
/setup portal ...with a guided flow, live preview and tab completion) come with the separate changesetup-portal-command.Tests
DiscTest(including an 8-block elytra step over a 1-block disc),BoxTest,PortalValidatorTestPortalGsonAdapterTest,LobbyMapPortalsTest,MapProviderPortalTest,LobbyPortalsBeanTestPortalIndexTest,PortalTriggerTest,PortalModuleTest,PortalModuleLeakTestVariantStartTestin both apps./gradlew buildis green.Not tested: a live CloudNet transfer.
OpenSpec
openspec/changes/lobby-portalshttps://claude.ai/code/session_01A7aAe2E6wAEbi6jjnyPTWH