Version: 0.10.0 | 64 pages across 3 registries
Architecture documentation for the HyperFactions GUI system using Hytale's CustomUI.
HyperFactions uses Hytale's InteractiveCustomUIPage system with:
- GuiManager - Central coordinator (page registration + delegation to openers)
- 3 Page Openers - FactionPageOpener, AdminPageOpener, NewPlayerPageOpener
- 3 Page Registries - Type-safe navigation between pages
- UIPaths - Centralized UI template path constants
- NavBarUtil + NavEntry - Shared navigation bar logic
- Data Models - Records for page state
- Shared Components - Reusable modals and UI elements
- Help System - Integrated help pages
- Real-Time Updates - ActivePageTracker for live data refresh
stateDiagram-v2
[*] --> HasFaction: /f (has faction)
[*] --> NoFaction: /f (no faction)
[*] --> Admin: /f admin
state HasFaction {
FactionMain --> Members
FactionMain --> Relations
FactionMain --> Territory
FactionMain --> Settings
FactionMain --> Economy
FactionMain --> Help
}
state NoFaction {
NewPlayerMain --> CreateFaction
NewPlayerMain --> BrowseFactions
NewPlayerMain --> ViewInvites
NewPlayerMain --> NewPlayerHelp
}
state Admin {
AdminMain --> FactionsList
AdminMain --> ZoneManagement
AdminMain --> ConfigEditor
AdminMain --> BackupManager
AdminMain --> DebugTools
}
GuiManager (registration + delegation)
│
├─► FactionPageOpener (35 methods)
│ ├─► FactionMainPage (dashboard)
│ ├─► FactionMembersPage
│ ├─► FactionRelationsPage
│ ├─► FactionSettingsPage
│ ├─► TreasuryPage
│ └─► ... (15+ pages)
│
├─► NewPlayerPageOpener (8 methods)
│ ├─► NewPlayerBrowsePage
│ ├─► CreateFactionPage
│ ├─► InvitesPage
│ ├─► HelpPage
│ └─► ... (5+ pages)
│
├─► AdminPageOpener (38 methods)
│ ├─► AdminMainPage
│ ├─► AdminZoneMapPage
│ ├─► AdminFactionsPage
│ └─► ... (12+ pages)
│
└─► Shared Components
├─► UIPaths (centralized template paths)
├─► NavBarUtil + NavEntry (shared nav logic)
├─► InputModal
├─► ColorPickerModal
└─► ConfirmationModal
| Class | Path | Purpose |
|---|---|---|
| GuiManager | gui/GuiManager.java |
Central coordinator (registration + delegation) |
| FactionPageOpener | gui/FactionPageOpener.java |
Faction page opening (35 methods) |
| AdminPageOpener | gui/AdminPageOpener.java |
Admin page opening (38 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 |
| FactionPageRegistry | gui/faction/FactionPageRegistry.java |
Faction page navigation |
| NewPlayerPageRegistry | gui/newplayer/NewPlayerPageRegistry.java |
New player page navigation |
| AdminPageRegistry | gui/admin/AdminPageRegistry.java |
Admin page navigation |
| NavBarUtil | gui/shared/NavBarUtil.java |
Shared nav bar button builder |
| NavEntry | gui/shared/NavEntry.java |
Navigation entry interface |
Central coordinator that handles page registration and delegates page opening to focused opener classes:
public class GuiManager {
private final Supplier<FactionManager> factionManager;
// ... other manager suppliers
private final FactionPageOpener factionPageOpener;
private final AdminPageOpener adminPageOpener;
private final NewPlayerPageOpener newPlayerPageOpener;
public GuiManager(...) {
// 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:
FactionMainPage (dashboard)
│
├─► FactionMembersPage
│ └─► PlayerInfoPage
│ └─► TransferConfirmPage / LeaveConfirmPage
│
├─► FactionRelationsPage
│ └─► SetRelationModalPage
│
├─► FactionSettingsPage
│ ├─► RenameModalPage
│ ├─► DescriptionModalPage
│ ├─► TagModalPage
│ └─► ColorPickerPage
│
├─► ChunkMapPage
│
├─► FactionBrowserPage
│ └─► FactionInfoPage (other faction)
│
├─► LogsViewerPage
│
└─► FactionHelpPage
For players without a faction:
MainMenuPage
│
├─► CreateFactionStep1Page
│ └─► CreateFactionStep2Page
│
├─► InvitesPage
│ └─► Accept invite → joins faction
│
├─► NewPlayerBrowsePage
│ └─► Request to join
│
├─► NewPlayerMapPage
│
└─► HelpPage
For players with admin permission:
AdminMainPage
│
├─► AdminDashboardPage (stats)
│
├─► AdminFactionsPage
│ ├─► AdminFactionInfoPage
│ ├─► AdminFactionMembersPage
│ ├─► AdminFactionRelationsPage
│ ├─► AdminFactionSettingsPage
│ └─► AdminDisbandConfirmPage
│
├─► AdminZoneMapPage
│ ├─► CreateZoneWizardPage
│ └─► AdminZoneSettingsPage
│ └─► AdminZoneIntegrationFlagsPage
│
├─► AdminConfigPage
│
├─► AdminBackupsPage
│
├─► AdminUpdatesPage
│
└─► AdminHelpPage
Each flow uses a registry for type-safe navigation:
gui/faction/FactionPageRegistry.java
public class FactionPageRegistry {
public enum Entry {
MAIN,
MEMBERS,
RELATIONS,
SETTINGS,
MAP,
BROWSER,
LOGS,
HELP,
// ... modals
PLAYER_INFO,
RENAME_MODAL,
COLOR_PICKER,
DISBAND_CONFIRM,
LEAVE_CONFIRM,
TRANSFER_CONFIRM
}
public static void openPage(
Entry entry,
Player player,
Ref<EntityStore> ref,
Store<EntityStore> store,
PlayerRef playerRef,
Object... args) {
InteractiveCustomUIPage page = createPage(entry, player, ref, store, playerRef, args);
PageManager pageManager = player.getPageManager();
pageManager.openPage(page);
}
private static InteractiveCustomUIPage createPage(Entry entry, ...) {
return switch (entry) {
case MAIN -> new FactionMainPage(playerRef, ref, store);
case MEMBERS -> new FactionMembersPage(playerRef, ref, store);
case RELATIONS -> new FactionRelationsPage(playerRef, ref, store);
// ...
};
}
}Each page extends InteractiveCustomUIPage:
public class FactionMainPage extends InteractiveCustomUIPage {
private final PlayerRef playerRef;
private final Ref<EntityStore> ref;
private final Store<EntityStore> store;
public FactionMainPage(PlayerRef playerRef, Ref<EntityStore> ref, Store<EntityStore> store) {
super("hyperfactions:faction_main"); // UI definition ID
this.playerRef = playerRef;
this.ref = ref;
this.store = store;
}
@Override
public void init(Data data) {
// Populate initial page data
FactionMainData pageData = buildData();
data.set(pageData);
}
@Override
public void handleEvent(String event, Data data) {
// Handle button clicks
switch (event) {
case "members_clicked" -> navigateToMembers();
case "relations_clicked" -> navigateToRelations();
case "claim_clicked" -> performClaim();
// ...
}
}
private void navigateToMembers() {
FactionPageRegistry.openPage(Entry.MEMBERS, player, ref, store, playerRef);
}
}Pages use records for their data models:
// gui/faction/data/FactionMainData.java
public record FactionMainData(
String factionName,
String factionTag,
String factionColor,
int memberCount,
int maxMembers,
int claimCount,
int maxClaims,
double factionPower,
double maxPower,
boolean isLeader,
boolean isOfficer,
List<MemberEntry> onlineMembers
) {
public record MemberEntry(
String username,
String role,
boolean online
) {}
}gui/shared/component/InputModal.java
Generic text input modal:
public class InputModal {
public static void show(
Player player,
String title,
String placeholder,
String currentValue,
Consumer<String> onSubmit,
Runnable onCancel) {
// Open modal with callback handlers
}
}gui/shared/component/ColorPickerModal.java
Color selection grid:
public class ColorPickerModal {
// 16 Minecraft color codes (0-9, a-f)
private static final String[] COLORS = {
"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"
};
public static void show(
Player player,
String currentColor,
Consumer<String> onSelect) {
// Open color grid modal
}
}gui/shared/component/ConfirmationModal.java
Yes/No confirmation dialog:
public class ConfirmationModal {
public static void show(
Player player,
String title,
String message,
String confirmText,
String cancelText,
Runnable onConfirm,
Runnable onCancel) {
// Open confirmation dialog
}
}// From FactionMainPage
private void onMembersClicked() {
FactionPageRegistry.openPage(
Entry.MEMBERS,
player, ref, store, playerRef
);
}// Open player info for specific player
private void onMemberClicked(UUID targetUuid) {
FactionPageRegistry.openPage(
Entry.PLAYER_INFO,
player, ref, store, playerRef,
targetUuid // Additional argument
);
}// Close current page (returns to previous)
private void onBackClicked() {
player.getPageManager().closePage();
}// Show rename modal, then return to settings
private void onRenameClicked() {
InputModal.show(
player,
"Rename Faction",
"New name",
currentName,
newName -> {
// Process rename
factionManager.renameFaction(factionId, newName);
// Refresh settings page
FactionPageRegistry.openPage(Entry.SETTINGS, ...);
},
() -> {
// Cancelled - stay on current page
}
);
}gui/
├── GuiManager.java # Central coordinator (registration + delegation)
├── FactionPageOpener.java # Faction page opening methods (35 methods)
├── AdminPageOpener.java # Admin page opening methods (38 methods)
├── NewPlayerPageOpener.java # New player page opening methods (8 methods)
├── UIPaths.java # Centralized UI template path constants
├── GuiType.java # Page type enum
├── ActivePageTracker.java # Live data refresh tracking
├── RefreshablePage.java # Refreshable page interface
├── GuiUpdateService.java # GUI update coordination
│
├── faction/ # Faction member pages
│ ├── FactionPageRegistry.java # Navigation registry
│ ├── NavBarHelper.java # Faction navigation bar
│ ├── ChunkMapAsset.java # Chunk map asset
│ ├── page/ # Page implementations
│ │ ├── FactionMainPage.java
│ │ ├── FactionMembersPage.java
│ │ ├── FactionRelationsPage.java
│ │ ├── FactionSettingsPage.java
│ │ ├── FactionBrowserPage.java
│ │ ├── FactionDashboardPage.java
│ │ ├── FactionHelpPage.java
│ │ ├── FactionInvitesPage.java
│ │ ├── FactionModulesPage.java
│ │ ├── FactionChatPage.java
│ │ ├── FactionLeaderboardPage.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/ # Data records
│ ├── FactionMainData.java
│ ├── FactionMembersData.java
│ ├── FactionRelationsData.java
│ └── ...
│
├── admin/ # Admin pages (registry + pages + data)
│ ├── AdminPageRegistry.java
│ ├── AdminNavBarHelper.java
│ ├── page/ # Admin page implementations
│ │ ├── AdminMainPage.java
│ │ ├── AdminDashboardPage.java
│ │ ├── AdminFactionsPage.java
│ │ ├── AdminFactionInfoPage.java
│ │ ├── AdminFactionMembersPage.java
│ │ ├── AdminFactionRelationsPage.java
│ │ ├── AdminFactionSettingsPage.java
│ │ ├── AdminPlayersPage.java
│ │ ├── AdminPlayerInfoPage.java
│ │ ├── AdminZoneMapPage.java
│ │ ├── AdminZonePage.java
│ │ ├── AdminZoneSettingsPage.java
│ │ ├── AdminZoneIntegrationFlagsPage.java
│ │ ├── CreateZoneWizardPage.java
│ │ ├── ZoneRenameModalPage.java
│ │ ├── ZoneChangeTypeModalPage.java
│ │ ├── AdminConfigPage.java
│ │ ├── AdminBackupsPage.java
│ │ ├── AdminUpdatesPage.java
│ │ ├── AdminActivityLogPage.java
│ │ ├── AdminActionsPage.java
│ │ ├── AdminEconomyPage.java
│ │ ├── AdminEconomyAdjustPage.java
│ │ ├── AdminVersionPage.java
│ │ ├── AdminZonePropertiesPage.java
│ │ ├── AdminHelpPage.java
│ │ ├── AdminDisbandConfirmPage.java
│ │ └── AdminUnclaimAllConfirmPage.java
│ └── data/ # Admin data records
│ ├── AdminMainData.java
│ ├── AdminDashboardData.java
│ └── ...
│
├── newplayer/ # New player flow (registry + pages + data)
│ ├── NewPlayerPageRegistry.java # New player navigation
│ ├── NewPlayerNavBarHelper.java # New player navigation bar
│ ├── page/ # New player page implementations
│ │ ├── CreateFactionPage.java
│ │ ├── InvitesPage.java
│ │ ├── HelpPage.java
│ │ ├── NewPlayerMapPage.java
│ │ └── NewPlayerBrowsePage.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
│ │ └── ConfirmationModal.java
│ ├── page/
│ │ ├── MainMenuPage.java
│ │ ├── FactionInfoPage.java
│ │ ├── PlaceholderPage.java
│ │ ├── RenameModalPage.java
│ │ ├── DescriptionModalPage.java
│ │ └── TagModalPage.java
│ └── data/
│ ├── NavAwareData.java
│ ├── MainMenuData.java
│ └── ...
│
├── help/ # Help system
│ ├── HelpCategory.java
│ ├── HelpTopic.java
│ ├── HelpRegistry.java
│ ├── data/
│ │ └── HelpPageData.java
│ └── page/
│ └── HelpMainPage.java
│
└── test/ # Test pages
└── ButtonTestPage.java
Pages check permissions before sensitive operations:
public class FactionSettingsPage extends InteractiveCustomUIPage {
@Override
public void handleEvent(String event, Data data) {
if (event.equals("disband_clicked")) {
// Check if player is leader
if (!isLeader(playerRef.getUuid())) {
showError("Only the faction leader can disband.");
return;
}
// Check permission
if (!hasPermission(playerRef.getUuid(), Permissions.DISBAND)) {
showError("You don't have permission to disband.");
return;
}
// Show confirmation
FactionPageRegistry.openPage(Entry.DISBAND_CONFIRM, ...);
}
}
}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.
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.
-
Create data record in appropriate
data/package:public record NewFeatureData( String title, List<ItemEntry> items ) {}
-
Create page class in appropriate
page/package:public class NewFeaturePage extends InteractiveCustomUIPage { // Implementation }
-
Add to registry enum and switch:
// In FactionPageRegistry public enum Entry { // ... NEW_FEATURE } private static InteractiveCustomUIPage createPage(Entry entry, ...) { return switch (entry) { // ... case NEW_FEATURE -> new NewFeaturePage(playerRef, ref, store); }; }
-
Add navigation from existing pages:
private void onNewFeatureClicked() { FactionPageRegistry.openPage(Entry.NEW_FEATURE, ...); }
| Class | Path |
|---|---|
| GuiColors | gui/GuiColors.java |
| GuiManager | gui/GuiManager.java |
| FactionPageOpener | gui/FactionPageOpener.java |
| AdminPageOpener | gui/AdminPageOpener.java |
| NewPlayerPageOpener | gui/NewPlayerPageOpener.java |
| UIPaths | gui/UIPaths.java |
| FactionPageRegistry | gui/faction/FactionPageRegistry.java |
| NewPlayerPageRegistry | gui/newplayer/NewPlayerPageRegistry.java |
| AdminPageRegistry | gui/admin/AdminPageRegistry.java |
| NavBarUtil | gui/shared/NavBarUtil.java |
| NavEntry | gui/shared/NavEntry.java |
| NavBarHelper | gui/faction/NavBarHelper.java |
| InputModal | gui/shared/component/InputModal.java |