Skip to content

Server screen manager

ServerScreenManager is the server-side entry point for in-world interactive screens. You hand it a placement (centre, normal, width, height) and a ScreenContent tree, and it pushes the right network payload to either one player or every player in a dimension. Screen ids are stable per purpose — calling showWithId with the same id replaces the previous screen rather than stacking.

  • You want a clickable settings panel mounted on a wall.
  • You’re showing a per-player results screen at the end of a round.
  • You’re putting a world-wide informational sign in front of a hub pad.
package me.zlex.conduit.screen;
public final class ServerScreenManager {
// Per-player, auto-assigned id (starts at 1000):
public static int show(ServerPlayer player, Vec3 center, Vec3 normal,
float width, float height, ScreenContent content);
public static int show(ServerPlayer player, ScreenPlacement place,
float width, float height, ScreenContent content);
// Per-player, stable id (replaces any prior screen with the same id):
public static void showWithId(ServerPlayer player, int id,
Vec3 center, Vec3 normal,
float width, float height, ScreenContent content);
// World-wide — sent to every player in the dimension + re-sent on join:
public static int worldShow(MinecraftServer server, ResourceKey<Level> dim,
Vec3 center, Vec3 normal,
float width, float height, ScreenContent content);
public static void worldHide(MinecraftServer server, int id);
public static void hide(ServerPlayer player, int id);
// Click + text-input routing — one handler per screen id:
public static void onButton(int screenId, BiConsumer<ServerPlayer, String> handler);
public static void onTextInput(int screenId, TextInputHandler handler);
public static void init(); // call once from your ModInitializer
}
import me.zlex.conduit.screen.ScreenContent;
import me.zlex.conduit.screen.ServerScreenManager;
import me.zlex.conduit.screen.placement.Placements;
import me.zlex.conduit.lobby.screen.builtin.LobbyScreen;
import net.minecraft.server.level.ServerPlayer;
public final class MyLobbyUi {
private static final int SCREEN_ID = 7100; // pick outside the 1000+ auto range
public static void showLobbyTo(ServerPlayer host, List<String> roster) {
ScreenContent content = LobbyScreen.build(MyMod.GAME, roster, List.of());
var place = Placements.inFrontOf(host, 2.5f);
ServerScreenManager.showWithId(host, SCREEN_ID,
place.center(), place.normal(), 4.5f, 4.0f, content);
ServerScreenManager.onButton(SCREEN_ID, (clicker, buttonId) -> {
if (LobbyScreen.BTN_START.equals(buttonId)) MyRound.start(clicker);
if (LobbyScreen.BTN_LEAVE.equals(buttonId)) MyLobby.leave(clicker);
});
}
}
  • Vanilla clients silently ignore screen payloads. Pair every panel with a chat-text fallback if you care about non-engine clients.
  • Auto-assigned ids start at 1000. If you want a stable id (so the same panel can be re-pushed without stacking), pick one outside 1000.. — engine code does this with constants like 7100.
  • World-wide screens persist in a server-side registry until you call worldHide — they survive /reload and are re-pushed to joining players.