---
title: Portal Systems
type: docs
weight: 2
---

Portal systems handle the runtime logic for portal tracking, void events, and instance management.

**Package:** `com.hypixel.hytale.builtin.portals.systems`

## Portal Tracker Systems

### TrackerSystem

Tracks players entering and leaving portal worlds:

```java
public class TrackerSystem extends RefSystem<EntityStore> {
    // Called when player enters portal world
    @Override
    public void onEntityAdded(Ref<EntityStore> ref, AddReason reason,
                              Store<EntityStore> store,
                              CommandBuffer<EntityStore> commandBuffer) {
        PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
        if (!portalWorld.exists()) return;

        // Send portal UI to player
        PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
        UpdatePortal packet = portalWorld.createFullPacket(world);
        playerRef.getPacketHandler().write(packet);
    }

    // Called when player leaves portal world
    @Override
    public void onEntityRemove(Ref<EntityStore> ref, RemoveReason reason,
                               Store<EntityStore> store,
                               CommandBuffer<EntityStore> commandBuffer) {
        // Clear UI state
        playerRef.getPacketHandler().write(new UpdatePortal(null, null));
        portalWorld.getSeesUi().remove(playerRef.getUuid());
    }

    @Override
    public Query<EntityStore> getQuery() {
        return Query.and(Player.getComponentType(), PlayerRef.getComponentType());
    }
}
```

### UiTickingSystem

Updates portal UI every second:

```java
public class UiTickingSystem extends DelayedEntitySystem<EntityStore> {
    public UiTickingSystem() {
        super(1.0f);  // Tick every 1 second
    }

    @Override
    public void tick(float dt, int index, ArchetypeChunk<EntityStore> archetypeChunk,
                     Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer) {
        PortalWorld portalWorld = store.getResource(PortalWorld.getResourceType());
        if (!portalWorld.exists()) return;

        // Send updated time to player
        UpdatePortal packet = portalWorld.createUpdatePacket(world);
        playerRef.getPacketHandler().write(packet);
    }
}
```

## Portal Destination Systems

### PortalInvalidDestinationSystem

Handles portals with invalid destinations:

```java
public class PortalInvalidDestinationSystem {
    // Turns off portals when destination world closes
    public static void turnOffPortalsInWorld(World world, World closedWorld) {
        // Find all portals pointing to closedWorld
        // Set their destination to null
        // Update block state to "off"
    }
}
```

### CloseWorldWhenBreakingDeviceSystems

Handles portal device destruction:

```java
// When portal device component is removed
public class ComponentRemoved extends System<ChunkStore> {
    // Close the destination world
}

// When portal block entity is removed
public class EntityRemoved extends System<ChunkStore> {
    // Close the destination world
}
```

## Void Event Systems

### VoidEventRefSystem

Manages void event entity references:

```java
public class VoidEventRefSystem extends System<EntityStore> {
    // Tracks void event entity lifecycle
    // Updates PortalWorld.voidEventRef
}
```

### VoidEventStagesSystem

Progresses through void event stages:

```java
public class VoidEventStagesSystem extends System<EntityStore> {
    // Checks elapsed time
    // Activates next stage when time threshold reached
    // Updates VoidEvent.activeStage
}
```

### VoidInvasionPortalsSpawnSystem

Spawns invasion portals during void events:

```java
public class VoidInvasionPortalsSpawnSystem extends System<EntityStore> {
    // Creates void spawner entities
    // Uses spatial hash grid to maintain minimum distance
}
```

### VoidSpawnerSystems.Instantiate

Instantiates void spawner entities:

```java
public class Instantiate extends System<EntityStore> {
    // Creates spawner entity from config
    // Adds to VoidEvent's spatial grid
}
```

### StartVoidEventInFragmentSystem

Initiates void events in portal fragments:

```java
public class StartVoidEventInFragmentSystem extends System<EntityStore> {
    // Checks if void invasion should start
    // Creates VoidEvent entity
    // Sets up first stage
}
```

## Curse Systems

### DiedInPortalSystem

Tracks player deaths in portal worlds:

```java
public class DiedInPortalSystem extends System<EntityStore> {
    // On player death in portal world:
    // - Add player UUID to diedInWorld set
    // - Prevents re-entry
}
```

### CurseItemDropsSystem

Marks dropped items as cursed:

```java
public class CurseItemDropsSystem extends System<EntityStore> {
    // Items dropped in portal world become cursed
    // Cursed items are lost on death
}
```

### DeleteCursedItemsOnSpawnSystem

Removes cursed items when player spawns:

```java
public class DeleteCursedItemsOnSpawnSystem extends System<EntityStore> {
    // When player respawns after dying in portal
    // Remove all cursed items from inventory
}
```

## Portal Interactions

### EnterPortalInteraction

Handles entering a portal:

```java
public class EnterPortalInteraction extends SimpleBlockInteraction {
    // Minimum time before allowing portal use
    public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofMillis(3000L);

    @Override
    protected void interactWithBlock(...) {
        // Check portal device exists
        // Verify destination world is alive
        // Check player hasn't died in target world
        // Teleport player to instance
    }
}
```

Target world states:
- `OKAY` - Can enter
- `WORLD_DEAD` - Destination closed
- `DIED_IN_WORLD` - Player died there
- `NO_SPAWN_AVAILABLE` - No spawn point

### ReturnPortalInteraction

Handles returning from a portal world:

```java
public class ReturnPortalInteraction extends SimpleBlockInteraction {
    // Minimum time before allowing return
    public static final Duration MINIMUM_TIME_IN_WORLD = Duration.ofSeconds(15L);

    // Warning shown before timer expires
    public static final Duration WARNING_TIME = Duration.ofSeconds(4L);

    @Override
    protected void interactWithBlock(...) {
        // Check minimum time elapsed
        // Uncurse all items
        // Exit instance
    }
}
```

## System Registration

All systems are registered in PortalsPlugin setup:

```java
// ChunkStore systems
this.getChunkStoreRegistry().registerSystem(new PortalInvalidDestinationSystem());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.ComponentRemoved());
this.getChunkStoreRegistry().registerSystem(new CloseWorldWhenBreakingDeviceSystems.EntityRemoved());

// EntityStore systems
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.TrackerSystem());
this.getEntityStoreRegistry().registerSystem(new PortalTrackerSystems.UiTickingSystem());
this.getEntityStoreRegistry().registerSystem(new DiedInPortalSystem());
this.getEntityStoreRegistry().registerSystem(new CurseItemDropsSystem());
this.getEntityStoreRegistry().registerSystem(new DeleteCursedItemsOnSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventRefSystem());
this.getEntityStoreRegistry().registerSystem(new VoidInvasionPortalsSpawnSystem());
this.getEntityStoreRegistry().registerSystem(new VoidSpawnerSystems.Instantiate());
this.getEntityStoreRegistry().registerSystem(new StartVoidEventInFragmentSystem());
this.getEntityStoreRegistry().registerSystem(new VoidEventStagesSystem());
```

## Interaction Registration

Portal interactions are registered as codec types:

```java
this.getCodecRegistry(Interaction.CODEC)
    .register("Portal", EnterPortalInteraction.class, EnterPortalInteraction.CODEC)
    .register("PortalReturn", ReturnPortalInteraction.class, ReturnPortalInteraction.CODEC);
```
