How-to: Add a Custom League

A league in Library of Exile represents a season or game mode that occupies a specific server-side context (dimension, structure, or positional check). Leagues are hardcoded — they are not JSON-driven.


1. Implement League

public class MyLeague extends League {

    public static final String ID = "my_mod:my_league";

    public MyLeague() {
        this.id = ID;
    }

    /**
     * Return true when the given position is considered "inside" this league.
     * Common checks: dimension key, structure tags, or custom capability data.
     */
    @Override
    public boolean isInSide(ServerLevel level, BlockPos pos) {
        // Example: active whenever the player is in your custom dimension.
        return level.dimension().equals(
            ResourceKey.create(Registries.DIMENSION, new ResourceLocation("my_mod", "my_dimension"))
        );
    }

    @Override
    public ChatFormatting getTextColor() {
        return ChatFormatting.LIGHT_PURPLE;
    }

    @Override
    public String modid() {
        return "my_mod";
    }

    @Override
    public String locName() {
        return "My League";
    }

    // ExileRegistry contract:
    @Override
    public ExileRegistryType getExileRegistryType() {
        return LibDatabase.LEAGUE;
    }

    @Override
    public int Weight() { return 1000; }
}

2. Register the league

Create an ExileRegistryEventClass for the LEAGUE type:

public class MyLeagueRegistrar extends ExileRegistryEventClass {

    @Override
    public ExileRegistryType getType() {
        return LibDatabase.LEAGUE;
    }

    @Override
    public void init(ExileRegistryEvent e) {
        e.add(new MyLeague(), ExileRegistrationInfo.of("my_mod"));
    }
}

Return it from getRegisterEvents() in your OrderedModConstructor:

@Override
public List<ExileRegistryEventClass> getRegisterEvents() {
    return List.of(new MyLeagueRegistrar());
}

3. Look up the active league

At runtime, find the league that is active at any server-side position:

ServerLevel level = /* ... */;
BlockPos pos = player.blockPosition();

League league = League.getFromPosition(level, pos);

if (league.GUID().equals(MyLeague.ID)) {
    // Player is in My League
}

If no league matches, League.getFromPosition returns LibLeagues.INSTANCE.EMPTY.get().


4. Work with CurrentLeague

CurrentLeague provides richer context for a player inside a map dimension:

Optional<CurrentLeague> current = CurrentLeague.get(level, pos);

current.ifPresent(cl -> {
    // cl.dimension — the MapDimensionInfo for the current dimension
    // cl.structure — Optional<MapStructure> if inside a structure
    // cl.connectedFrom — Optional connection data if teleported in
});

5. Listen for league teleport events

ApiForgeEvents.registerForgeEvent(OnTeleportToLeagueEvent.class, event -> {
    ServerPlayer player = event.getPlayer();
    League destination  = event.getLeague();

    if (destination.GUID().equals(MyLeague.ID)) {
        // Prepare the player: apply buffs, reset state, etc.
    }
});

Notes

  • Leagues use SyncTime.NEVER — they are server-only. Do not assume client-side access.
  • Only one League can be "active" at a position at a time; getFromPosition returns the first match in registry order. Make your isInSide conditions mutually exclusive.
  • The EmptyLeague (GUID = "empty") is always present as the fallback. Never use null for a league reference.