Version: 0.13.0 | 69 page classes across 3 registries
Architecture documentation for the HyperFactions GUI system using Hytale's CustomUI.
HyperFactions uses Hytale's InteractiveCustomUIPage<T> system with:
- GuiManager - Central coordinator (page registration + delegation to openers)
- 3 Page Openers - FactionPageOpener, AdminPageOpener, NewPlayerPageOpener
- 3 Page Registries - Singleton registries with record-based entries for type-safe navigation
- UIPaths - Centralized UI template path constants
- NavBarUtil + NavEntry - Shared navigation bar logic
- Data Models - Codec-based event data classes for page interactions
- Shared Components - Reusable modals and UI elements (Builder pattern)
- Help System - Integrated help pages with rich text rendering
- Real-Time Updates - ActivePageTracker + GuiUpdateService for live data refresh
stateDiagram-v2
[*] --> HasFaction: /f (has faction)
[*] --> NoFaction: /f (no faction)
[*] --> Admin: /f admin
state HasFaction {
Dashboard --> Members
Dashboard --> Chat
Dashboard --> Invites
Dashboard --> Browser
Dashboard --> Map
Dashboard --> Leaderboard
Dashboard --> Relations
Dashboard --> Treasury
Dashboard --> Settings
Dashboard --> Logs
Dashboard --> Help
}
state NoFaction {
NewPlayerBrowse --> CreateFaction
NewPlayerBrowse --> ViewInvites
NewPlayerBrowse --> NewPlayerMap
NewPlayerBrowse --> Leaderboard
NewPlayerBrowse --> NewPlayerHelp
}
state Admin {
AdminDashboard --> AdminActions
AdminDashboard --> AdminFactions
AdminDashboard --> AdminPlayers
AdminDashboard --> AdminEconomy
AdminDashboard --> AdminZones
AdminDashboard --> AdminConfig
AdminDashboard --> AdminBackups
AdminDashboard --> AdminActivityLog
AdminDashboard --> AdminUpdates
AdminDashboard --> AdminHelp
AdminDashboard --> AdminVersion
}
GuiManager (registration + delegation)
|
|--- FactionPageOpener (37 methods)
| |--- FactionMainPage / FactionDashboardPage
| |--- FactionMembersPage
| |--- FactionRelationsPage
| |--- FactionSettingsPage
| |--- TreasuryPage
| |--- FactionChatPage
| |--- FactionLeaderboardPage
| |--- PlayerInfoPage, FactionInfoPage
| '--- ... (24 faction page classes total)
|
|--- NewPlayerPageOpener (8 methods)
| |--- NewPlayerBrowsePage
| |--- CreateFactionPage
| |--- InvitesPage
| |--- NewPlayerMapPage
| '--- HelpPage (5 newplayer page classes)
|
|--- AdminPageOpener (41 methods)
| |--- AdminMainPage / AdminDashboardPage
| |--- AdminFactionsPage
| |--- AdminZoneMapPage
| |--- AdminConfigPage
| '--- ... (30 admin page classes total)
|
'--- Shared Components
|--- UIPaths (centralized template paths)
|--- NavBarUtil + NavEntry (shared nav logic)
|--- InputModal (Builder pattern)
'--- ConfirmationModal (Builder pattern)
| Class | Path | Purpose |
|---|---|---|
| GuiManager | gui/GuiManager.java |
Central coordinator (registration + delegation) |
| FactionPageOpener | gui/FactionPageOpener.java |
Faction page opening (37 methods) |
| AdminPageOpener | gui/AdminPageOpener.java |
Admin page opening (41 methods) |
| NewPlayerPageOpener | gui/NewPlayerPageOpener.java |
New player page opening (8 methods) |
| UIPaths | gui/UIPaths.java |
Centralized UI template path constants |
| GuiType | gui/GuiType.java |
Page type enumeration (NEW_PLAYER, FACTION_PLAYER, ADMIN) |
| GuiColors | gui/GuiColors.java |
GUI color constants |
| FactionPageRegistry | gui/faction/FactionPageRegistry.java |
Faction page navigation (singleton, record-based entries) |
| NewPlayerPageRegistry | gui/newplayer/NewPlayerPageRegistry.java |
New player page navigation (singleton, record-based entries) |
| AdminPageRegistry | gui/admin/AdminPageRegistry.java |
Admin page navigation (singleton, record-based entries) |
| NavBarUtil | gui/shared/NavBarUtil.java |
Shared nav bar button builder |
| NavEntry | gui/shared/NavEntry.java |
Navigation entry interface |
| ActivePageTracker | gui/ActivePageTracker.java |
Tracks which pages players have open |
| GuiUpdateService | gui/GuiUpdateService.java |
Bridges manager events to GUI refresh |
| RefreshablePage | gui/RefreshablePage.java |
Interface for pages supporting live refresh |
Central coordinator that handles page registration and delegates page opening to focused opener classes:
public class GuiManager {
private final Supplier<HyperFactions> plugin;
private final Supplier<FactionManager> factionManager;
private final Supplier<ClaimManager> claimManager;
private final Supplier<PowerManager> powerManager;
private final Supplier<RelationManager> relationManager;
private final Supplier<ZoneManager> zoneManager;
private final Supplier<TeleportManager> teleportManager;
private final Supplier<InviteManager> inviteManager;
private final Supplier<JoinRequestManager> joinRequestManager;
private final Supplier<Path> dataDir;
// ... other fields
private final FactionPageOpener factionPageOpener;
private final AdminPageOpener adminPageOpener;
private final NewPlayerPageOpener newPlayerPageOpener;
public GuiManager(Supplier<HyperFactions> plugin,
Supplier<FactionManager> factionManager,
... /* all manager suppliers */) {
// Register pages with all three registries
registerPages();
registerNewPlayerPages();
registerAdminPages();
// Initialize page opener delegates
this.factionPageOpener = new FactionPageOpener(this);
this.adminPageOpener = new AdminPageOpener(this);
this.newPlayerPageOpener = new NewPlayerPageOpener(this);
}
// All openXxx() methods delegate to the appropriate opener
public void openFactionMain(Player player, Ref<EntityStore> ref,
Store<EntityStore> store, PlayerRef playerRef) {
factionPageOpener.openFactionMain(player, ref, store, playerRef);
}
public void openAdminMain(Player player, Ref<EntityStore> ref,
Store<EntityStore> store, PlayerRef playerRef) {
adminPageOpener.openAdminMain(player, ref, store, playerRef);
}
}For players who belong to a faction:
FactionDashboardPage (dashboard, or FactionMainPage if no faction)
|
|--- FactionChatPage
|
|--- FactionMembersPage
| '--- PlayerInfoPage
| |--- TransferConfirmPage
| |--- LeaveConfirmPage / LeaderLeaveConfirmPage
| '--- FactionInfoPage (target player's faction)
|
|--- FactionInvitesPage (officers+)
|
|--- FactionBrowserPage
| '--- FactionInfoPage (other faction)
|
|--- ChunkMapPage
|
|--- FactionLeaderboardPage
|
|--- FactionRelationsPage
| '--- SetRelationModalPage
|
|--- TreasuryPage (if economy enabled)
| |--- TreasuryDepositModalPage
| |--- TreasuryTransferSearchPage
| | '--- TreasuryTransferConfirmPage
| '--- TreasurySettingsPage
|
|--- FactionSettingsPage
| |--- RenameModalPage
| |--- DescriptionModalPage
| |--- TagModalPage
| '--- DisbandConfirmPage
|
|--- FactionModulesPage
|
|--- LogsViewerPage
|
|--- HelpMainPage
|
'--- PlayerSettingsPage
For players without a faction:
NewPlayerBrowsePage (default landing page)
|
|--- CreateFactionPage
|
|--- InvitesPage
| '--- Accept invite -> joins faction
|
|--- NewPlayerMapPage (read-only territory view)
|
|--- FactionLeaderboardPage
|
|--- HelpMainPage
|
'--- PlayerSettingsPage
For players with admin permission:
AdminDashboardPage (stats)
|
|--- AdminActionsPage
|
|--- AdminFactionsPage
| |--- AdminFactionInfoPage
| |--- AdminFactionMembersPage
| |--- AdminFactionRelationsPage
| |--- AdminFactionSettingsPage
| |--- AdminDisbandConfirmPage
| '--- AdminUnclaimAllConfirmPage
|
|--- AdminPlayersPage
| '--- AdminPlayerInfoPage
|
|--- AdminEconomyPage (if economy enabled)
| |--- AdminEconomyAdjustPage
| '--- AdminBulkEconomyPage
|
|--- AdminZonePage
| |--- AdminZoneMapPage
| |--- AdminZoneSettingsPage
| | '--- AdminZoneIntegrationFlagsPage
| |--- AdminZonePropertiesPage
| |--- CreateZoneWizardPage
| |--- ZoneRenameModalPage
| '--- ZoneChangeTypeModalPage
|
|--- AdminConfigPage
| '--- ScalingTiersModalPage
|
|--- AdminBackupsPage
|
|--- AdminActivityLogPage
|
|--- AdminUpdatesPage
|
|--- AdminHelpPage
|
'--- AdminVersionPage
Each flow uses a singleton registry with record-based entries for type-safe navigation. Entries are registered in GuiManager's constructor via registerPages(), registerNewPlayerPages(), and registerAdminPages().
gui/faction/FactionPageRegistry.java
public final class FactionPageRegistry {
private static final FactionPageRegistry INSTANCE = new FactionPageRegistry();
public static FactionPageRegistry getInstance() { return INSTANCE; }
/**
* Each entry is a record (not an enum) with runtime registration.
*/
public record Entry(
@NotNull String id, // e.g., "dashboard", "members"
@NotNull String displayName, // UI display name
@Nullable String permission, // Required permission node (null = no check)
@NotNull PageSupplier guiSupplier,
boolean showsInNavBar,
boolean requiresFaction,
@Nullable FactionRole minimumRole, // Minimum faction role (null = no role check)
int order // Display order (lower = first)
) implements NavEntry, Comparable<Entry> { }
@FunctionalInterface
public interface PageSupplier {
@Nullable InteractiveCustomUIPage<?> create(
Player player, Ref<EntityStore> ref, Store<EntityStore> store,
PlayerRef playerRef, @Nullable Faction faction, GuiManager guiManager
);
}
public void registerEntry(@NotNull Entry entry) { ... }
public @Nullable Entry getEntry(@NotNull String id) { ... }
public @NotNull List<Entry> getEntries() { ... }
public @NotNull List<Entry> getNavBarEntries() { ... }
public @NotNull List<Entry> getAccessibleEntries(@NotNull PlayerRef playerRef, @Nullable Faction faction) { ... }
public @NotNull List<Entry> getAccessibleNavBarEntries(@NotNull PlayerRef playerRef, @Nullable Faction faction) { ... }
}Registered entries (in GuiManager.registerPages()):
| ID | Nav Bar | Requires Faction | Order |
|---|---|---|---|
dashboard |
Yes | No | 0 |
chat |
Yes | Yes | 1 |
members |
Yes | Yes | 2 |
invites |
Yes | Yes (Officer+) | 3 |
browser |
Yes | No | 4 |
map |
Yes | No | 5 |
leaderboard |
Yes | No | 6 |
relations |
Yes | Yes | 7 |
treasury |
Yes | Yes (if economy enabled) | 8 |
settings |
Yes | Yes | 9 |
logs |
Yes | Yes | 10 |
help |
Yes | No | 11 |
player_settings |
No (far right) | No | 99 |
admin |
No | No | 13 |
gui/admin/AdminPageRegistry.java
Same singleton + record pattern. Entry record has fields: id, displayName, permission, guiSupplier, showsInNavBar, order (no requiresFaction or minimumRole).
Registered entries (in GuiManager.registerAdminPages()):
| ID | Nav Bar | Order |
|---|---|---|
dashboard |
Yes | 0 |
actions |
Yes | 1 |
factions |
Yes | 2 |
players |
Yes | 3 |
economy |
Yes (if enabled) | 4 |
zones |
Yes | 5 |
config |
Yes | 6 |
backups |
Yes | 7 |
log |
Yes | 8 |
updates |
Yes | 9 |
help |
Yes | 10 |
version |
Yes | 11 |
gui/newplayer/NewPlayerPageRegistry.java
Same singleton + record pattern. Entry record has fields: id, displayName, permission, guiSupplier, showsInNavBar, order.
Registered entries (in GuiManager.registerNewPlayerPages()):
| ID | Nav Bar | Order |
|---|---|---|
browse |
Yes | 0 |
create |
Yes | 1 |
invites |
Yes | 2 |
map |
Yes | 3 |
leaderboard |
Yes | 4 |
help |
Yes | 5 |
player_settings |
No (far right) | 99 |
Each page extends InteractiveCustomUIPage<T> where T is a codec-based event data class. Pages override build() to render UI and handleDataEvent() to process interactions:
public class FactionMainPage extends InteractiveCustomUIPage<FactionPageData> {
private final PlayerRef playerRef;
private final FactionManager factionManager;
private final ClaimManager claimManager;
private final PowerManager powerManager;
private final TeleportManager teleportManager;
private final InviteManager inviteManager;
private final GuiManager guiManager;
public FactionMainPage(PlayerRef playerRef,
FactionManager factionManager,
ClaimManager claimManager, ...) {
super(playerRef, CustomPageLifetime.CanDismiss, FactionPageData.CODEC);
// ... assign fields
}
@Override
public void build(Ref<EntityStore> ref, UICommandBuilder cmd,
UIEventBuilder events, Store<EntityStore> store) {
// Append UI templates via cmd.append(UIPaths.FACTION_MAIN)
// Set text/properties via cmd.set("#ElementId.Property", value)
// Bind events via events.addEventBinding(...)
NavBarHelper.setupBar(playerRef, faction, PAGE_ID, cmd, events);
}
@Override
public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store,
FactionPageData data) {
super.handleDataEvent(ref, store, data);
// Handle nav bar events
if (NavBarHelper.handleNavEvent(data, player, ref, store, playerRef, faction, guiManager)) {
return;
}
// Handle page-specific button clicks
switch (data.button) {
case "CreateFaction" -> guiManager.openCreateFaction(player, ref, store, playerRef);
case "Home" -> handleHomeTeleport(...);
case "Leave" -> handleLeave(...);
// ...
}
}
}Pages use codec-based event data classes (not display records). These hold the event payload from button clicks:
// gui/faction/data/FactionMainData.java
public class FactionMainData {
public String button; // The button/action that triggered the event
public String factionId; // Target faction ID (if any)
public static final BuilderCodec<FactionMainData> CODEC = BuilderCodec
.builder(FactionMainData.class, FactionMainData::new)
.addField(new KeyedCodec<>("Button", Codec.STRING),
(data, value) -> data.button = value, data -> data.button)
.addField(new KeyedCodec<>("FactionId", Codec.STRING),
(data, value) -> data.factionId = value, data -> data.factionId)
.build();
}Many pages share FactionPageData as a common event data class with fields like button, factionId, playerId, etc.
gui/shared/component/InputModal.java
Generic text input modal using a Builder pattern. Renders into an existing page via render():
public class InputModal {
// Use the builder to create
public static Builder builder() { ... }
public static class Builder {
public Builder title(@NotNull String title) { ... }
public Builder label(@NotNull String label) { ... }
public Builder placeholder(@NotNull String placeholder) { ... }
public Builder currentValue(@Nullable String currentValue) { ... }
public Builder maxLength(int maxLength) { ... }
public Builder multiline(boolean multiline) { ... }
public Builder submitEvent(@NotNull String eventName) { ... }
public Builder submitEvent(@NotNull String eventName, @NotNull EventData data) { ... }
public Builder cancelEvent(@NotNull String eventName) { ... }
public InputModal build() { ... }
}
// Renders into the page's UI tree
public void render(@NotNull UICommandBuilder cmd, @NotNull UIEventBuilder events,
@NotNull String targetId) { ... }
// Quick helpers
public static InputModal rename(String currentName) { ... }
public static InputModal description(String currentDescription) { ... }
public static InputModal playerName() { ... }
}gui/shared/component/ConfirmationModal.java
Yes/No confirmation dialog using a Builder pattern. Renders into an existing page via render():
public class ConfirmationModal {
public static Builder builder() { ... }
public static class Builder {
public Builder title(@NotNull String title) { ... }
public Builder message(@NotNull String message) { ... }
public Builder confirmEvent(@NotNull String eventName) { ... }
public Builder confirmEvent(@NotNull String eventName, @NotNull EventData data) { ... }
public Builder cancelEvent(@NotNull String eventName) { ... }
public ConfirmationModal build() { ... }
}
public void render(@NotNull UICommandBuilder cmd, @NotNull UIEventBuilder events,
@NotNull String targetId) { ... }
// Quick helpers
public static ConfirmationModal dangerous(String title, String message, String confirmEvent) { ... }
public static ConfirmationModal safe(String title, String message, String confirmEvent) { ... }
}Navigation goes through GuiManager, which delegates to the appropriate page opener:
// From any page's handleDataEvent
switch (data.button) {
case "Members" -> guiManager.openFactionMembers(player, ref, store, playerRef, faction);
case "Settings" -> guiManager.openFactionSettings(player, ref, store, playerRef, faction);
case "BrowseFactions" -> guiManager.openFactionBrowser(player, ref, store, playerRef);
}Nav bar events are handled by helper classes:
// In handleDataEvent
if (NavBarHelper.handleNavEvent(data, player, ref, store, playerRef, faction, guiManager)) {
return; // Nav bar handled it
}// Close current page
guiManager.closePage(player, ref, store);Modals are separate page classes that navigate back to the parent page on completion:
// Open rename modal (navigates to RenameModalPage)
guiManager.openRenameModal(player, ref, store, playerRef, faction);
// In RenameModalPage.handleDataEvent, after rename succeeds:
guiManager.openFactionSettings(player, ref, store, playerRef, faction);gui/
|-- GuiManager.java # Central coordinator (registration + delegation)
|-- FactionPageOpener.java # Faction page opening methods (37 methods)
|-- AdminPageOpener.java # Admin page opening methods (41 methods)
|-- NewPlayerPageOpener.java # New player page opening methods (8 methods)
|-- UIPaths.java # Centralized UI template path constants
|-- GuiType.java # Page type enum (NEW_PLAYER, FACTION_PLAYER, ADMIN)
|-- GuiColors.java # GUI color constants
|-- ActivePageTracker.java # Live data refresh tracking
|-- RefreshablePage.java # Refreshable page interface
|-- GuiUpdateService.java # GUI update coordination
|
|-- faction/ # Faction member pages
| |-- FactionPageRegistry.java # Navigation registry (singleton, record entries)
| |-- NavBarHelper.java # Faction navigation bar
| |-- ChunkMapAsset.java # Chunk map asset generation
| |-- page/ # Page implementations (24 classes)
| | |-- FactionMainPage.java
| | |-- FactionDashboardPage.java
| | |-- FactionMembersPage.java
| | |-- FactionRelationsPage.java
| | |-- FactionSettingsPage.java
| | |-- FactionBrowserPage.java
| | |-- FactionHelpPage.java
| | |-- FactionInvitesPage.java
| | |-- FactionModulesPage.java
| | |-- FactionChatPage.java
| | |-- FactionLeaderboardPage.java
| | |-- ChunkMapPage.java
| | |-- LogsViewerPage.java
| | |-- PlayerInfoPage.java
| | |-- TreasuryPage.java
| | |-- TreasuryDepositModalPage.java
| | |-- TreasurySettingsPage.java
| | |-- TreasuryTransferSearchPage.java
| | |-- TreasuryTransferConfirmPage.java
| | |-- SetRelationModalPage.java
| | |-- DisbandConfirmPage.java
| | |-- LeaveConfirmPage.java
| | |-- LeaderLeaveConfirmPage.java
| | '-- TransferConfirmPage.java
| '-- data/ # Event data classes (21 classes)
| |-- FactionPageData.java # Shared event data (button, factionId, playerId, ...)
| |-- FactionMainData.java
| |-- FactionDashboardData.java
| |-- FactionMembersData.java
| |-- FactionRelationsData.java
| |-- FactionSettingsData.java
| |-- FactionBrowserData.java
| |-- FactionChatData.java
| |-- FactionModulesData.java
| |-- ChunkMapData.java
| |-- LogsViewerData.java
| |-- PlayerInfoData.java
| |-- TreasuryData.java
| |-- TreasurySettingsData.java
| |-- TreasuryTransferConfirmData.java
| |-- DepositModalData.java
| |-- TransferSearchData.java
| |-- SetRelationModalData.java
| |-- TransferConfirmData.java
| |-- LeaveConfirmData.java
| '-- LeaderLeaveConfirmData.java
|
|-- admin/ # Admin pages (registry + pages + data)
| |-- AdminPageRegistry.java # Admin navigation registry (singleton, record entries)
| |-- AdminNavBarHelper.java # Admin navigation bar
| |-- ConfigSnapshot.java # Config editing session state
| |-- ConfigValidator.java # Config input validation
| |-- page/ # Admin page implementations (30 classes)
| | |-- AdminMainPage.java
| | |-- AdminDashboardPage.java
| | |-- AdminActionsPage.java
| | |-- AdminFactionsPage.java
| | |-- AdminFactionInfoPage.java
| | |-- AdminFactionMembersPage.java
| | |-- AdminFactionRelationsPage.java
| | |-- AdminFactionSettingsPage.java
| | |-- AdminPlayersPage.java
| | |-- AdminPlayerInfoPage.java
| | |-- AdminEconomyPage.java
| | |-- AdminEconomyAdjustPage.java
| | |-- AdminBulkEconomyPage.java
| | |-- AdminZonePage.java
| | |-- AdminZoneMapPage.java
| | |-- AdminZoneSettingsPage.java
| | |-- AdminZoneIntegrationFlagsPage.java
| | |-- AdminZonePropertiesPage.java
| | |-- CreateZoneWizardPage.java
| | |-- ZoneRenameModalPage.java
| | |-- ZoneChangeTypeModalPage.java
| | |-- AdminConfigPage.java
| | |-- ScalingTiersModalPage.java
| | |-- AdminBackupsPage.java
| | |-- AdminActivityLogPage.java
| | |-- AdminUpdatesPage.java
| | |-- AdminVersionPage.java
| | |-- AdminHelpPage.java
| | |-- AdminDisbandConfirmPage.java
| | '-- AdminUnclaimAllConfirmPage.java
| '-- data/ # Admin data classes (29 classes)
| |-- AdminMainData.java
| |-- AdminDashboardData.java
| |-- AdminNavAwareData.java
| |-- AdminActionsData.java
| |-- AdminFactionsData.java
| |-- AdminFactionInfoData.java
| |-- AdminFactionMembersData.java
| |-- AdminFactionRelationsData.java
| |-- AdminFactionSettingsData.java
| |-- AdminPlayersData.java
| |-- AdminPlayerInfoData.java
| |-- AdminEconomyData.java
| |-- AdminEconomyAdjustData.java
| |-- AdminBulkEconomyData.java
| |-- AdminZoneData.java
| |-- AdminZoneMapData.java
| |-- AdminZoneSettingsData.java
| |-- AdminZonePropertiesData.java
| |-- ZoneRenameModalData.java
| |-- ZoneChangeTypeModalData.java
| |-- AdminConfigData.java
| |-- ScalingTiersData.java
| |-- AdminBackupsData.java
| |-- AdminActivityLogData.java
| |-- AdminUpdatesData.java
| |-- AdminVersionData.java
| |-- AdminHelpData.java
| |-- AdminDisbandConfirmData.java
| '-- AdminUnclaimAllConfirmData.java
|
|-- newplayer/ # New player flow (registry + pages + data)
| |-- NewPlayerPageRegistry.java # New player navigation (singleton, record entries)
| |-- NewPlayerNavBarHelper.java # New player navigation bar
| |-- page/ # New player page implementations (5 classes)
| | |-- NewPlayerBrowsePage.java
| | |-- CreateFactionPage.java
| | |-- InvitesPage.java
| | |-- NewPlayerMapPage.java
| | '-- HelpPage.java
| '-- data/ # New player data models
| '-- NewPlayerPageData.java
|
|-- shared/ # Shared components
| |-- NavEntry.java # Navigation entry interface
| |-- NavBarUtil.java # Shared nav bar button builder
| |-- component/
| | |-- InputModal.java # Text input modal (Builder pattern)
| | '-- ConfirmationModal.java # Yes/No confirmation (Builder pattern)
| |-- page/ # Shared page implementations (7 classes)
| | |-- MainMenuPage.java
| | |-- FactionInfoPage.java
| | |-- PlayerSettingsPage.java
| | |-- PlaceholderPage.java
| | |-- RenameModalPage.java
| | |-- DescriptionModalPage.java
| | '-- TagModalPage.java
| '-- data/ # Shared data models (9 classes)
| |-- NavAwareData.java
| |-- MainMenuData.java
| |-- CreateFactionData.java
| |-- PlayerSettingsData.java
| |-- PlaceholderData.java
| |-- RenameModalData.java
| |-- DescriptionModalData.java
| |-- DisbandConfirmData.java
| '-- TagModalData.java
|
|-- help/ # Help system
| |-- HelpCategory.java # Help category enum
| |-- HelpTopic.java # Help topic record (id, titleKey, entries, commands, category)
| |-- HelpEntry.java # Typed content entry record (type, messageKey, color)
| |-- HelpRegistry.java # Help content registry
| |-- HelpMessages.java # Key-based i18n string store (delegates to HFMessages)
| |-- HelpRichText.java # Inline markdown parser (**bold**, `command`, *italic*)
| |-- data/
| | '-- HelpPageData.java
| '-- page/
| '-- HelpMainPage.java
|
'-- test/ # Test pages
|-- ButtonTestPage.java
'-- MarkdownTestPage.java
Pages check permissions through the registry entry's permission field (checked during navigation) and through explicit checks in handleDataEvent:
public class FactionSettingsPage extends InteractiveCustomUIPage<FactionSettingsData> {
@Override
public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store,
FactionSettingsData data) {
super.handleDataEvent(ref, store, data);
Player player = store.getComponent(ref, Player.getComponentType());
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
switch (data.button) {
case "Disband" -> {
// Check if player is leader
FactionMember member = faction.getMember(playerRef.getUuid());
if (member != null && member.role() == FactionRole.LEADER) {
guiManager.openDisbandConfirm(player, ref, store, playerRef, faction);
}
}
// ...
}
}
}Global admin quick actions: K/D reset (per-player and server-wide), bulk operations. Accessible from admin nav bar.
Server-wide faction activity browser. Aggregates logs across all factions with filters for log type, player name, and time range (1h/24h/7d/all). Paginated with expandable entries showing actor, target, and details.
Server economy overview with sortable faction balance list. Shows total server economy, average balance, and per-faction treasury details. Links to AdminEconomyAdjustPage for individual adjustments.
Per-faction treasury adjustment modal. Supports set, add, and remove operations with admin audit logging. Opened from AdminEconomyPage.
Bulk economy operations page for batch treasury adjustments across multiple factions.
Displays mod version, server version, build info, and integration status for all 12 supported mods (HyperPerms, LuckPerms, VaultUnlocked, Ecotale, PAPI, WiFlow, OrbisGuard, HyperProtect-Mixin, OG-Mixins, Gravestones, HyBounty, MultipleHUD). Green/red status indicators.
Consolidated zone property editor. Edit zone name, type (SafeZone/WarZone), and notification settings (entry/leave title suppression, custom text) in a single page. Opened from AdminZonePage.
Sortable faction leaderboard with 5 sort modes: K/D (default), Power, Territory, Balance, Members. 10 entries per page with pagination. Top 3 get gold/silver/bronze rank colors. Own faction row highlighted. Accessible from nav bar and /f leaderboard.
Faction treasury management hub. Shows balance, recent transactions, and autopay status. Links to deposit, withdrawal, transfer, and settings sub-pages. Requires economy integration (Ecotale/VaultUnlocked).
Deposit modal with amount input and confirmation. Validates against player balance. Shows current treasury and player balance.
Treasury autopay and access settings. Configure auto-deposit percentage, withdrawal permissions per role, and transfer limits.
Inter-faction transfer search. Browse and search target factions for treasury transfers. Shows faction names with balance preview.
Transfer confirmation modal. Shows source faction, target faction, amount, and fee (if configured). Requires officer+ permission.
Runtime config editor with 11 tabs: Server, Chat, Announcements, Economy, Factions, Faction Perms, Worldmap, Worlds, Backup, Debug, Gravestones. Size-adaptive layouts (narrow/standard/wide), inline editing with boolean toggles, integer/double steppers, text fields, color pickers, enum dropdowns, and locale selectors. Edit session caching survives page close/reopen. Per-field validation with error highlighting. Uses ConfigSnapshot for applying changes and ConfigValidator for input bounds.
Paginated backup list with expand/collapse detail view per entry. Create manual backups with optional custom name. Restore with two-click confirmation and automatic safety backup. Delete with two-click confirmation. Backup type filter dropdown (All / Hourly / Daily / Weekly / Manual / Migration).
Two-column layout: HyperFactions (left) and HyperProtect Mixin (right). Shows current version, latest version, channel, build date, and update status. Single "Check for Updates" button checks both simultaneously. Download buttons appear when updates are available. Changelog display for HyperFactions updates. Rollback support with two-click confirmation.
Upkeep scaling tiers editor modal opened from AdminConfigPage Economy tab. Add/remove/reorder tiers with promote/demote buttons (disabled on first/last). Live cost example display.
-
Create event data class in appropriate
data/package:public class NewFeatureData { public String button; // ... other event fields public static final BuilderCodec<NewFeatureData> CODEC = BuilderCodec .builder(NewFeatureData.class, NewFeatureData::new) .addField(new KeyedCodec<>("Button", Codec.STRING), (data, value) -> data.button = value, data -> data.button) .build(); }
-
Create page class in appropriate
page/package:public class NewFeaturePage extends InteractiveCustomUIPage<NewFeatureData> { public NewFeaturePage(PlayerRef playerRef, ...) { super(playerRef, CustomPageLifetime.CanDismiss, NewFeatureData.CODEC); } @Override public void build(Ref<EntityStore> ref, UICommandBuilder cmd, UIEventBuilder events, Store<EntityStore> store) { cmd.append(UIPaths.NEW_FEATURE); NavBarHelper.setupBar(playerRef, faction, "new_feature", cmd, events); // ... render UI } @Override public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, NewFeatureData data) { super.handleDataEvent(ref, store, data); // ... handle events } }
-
Register in GuiManager within
registerPages():registry.registerEntry(new Entry( "new_feature", GuiKeys.Nav.NEW_FEATURE, null, // permission (player, ref, store, playerRef, faction, guiManager) -> new NewFeaturePage(playerRef, ...), true, // showsInNavBar true, // requiresFaction 12 // order ));
-
Add opener method in the appropriate PageOpener class:
// In FactionPageOpener public void openNewFeature(Player player, Ref<EntityStore> ref, Store<EntityStore> store, PlayerRef playerRef, Faction faction) { PageManager pageManager = player.getPageManager(); NewFeaturePage page = new NewFeaturePage(playerRef, ...); pageManager.openCustomPage(ref, store, page); }
-
Add delegation in GuiManager:
public void openNewFeature(Player player, Ref<EntityStore> ref, Store<EntityStore> store, PlayerRef playerRef, Faction faction) { factionPageOpener.openNewFeature(player, ref, store, playerRef, faction); }