Development Guide¶
This guide covers the architecture and development patterns used in HSM for contributors.
Architecture Overview¶
HSM uses a Bubble Tea-based TUI with a clean separation between:
- TUI Layer (
src/internal/tui/) - User interface, views, input handling - Backend Layer (
src/internal/hytale/) - Game-specific operations, server management, file I/O
Project Structure¶
hytale-server-manager/
├── src/
│ ├── cmd/
│ │ └── hytale-tui/
│ │ └── main.go # Entry point, CLI parsing
│ └── internal/
│ ├── tui/ # TUI layer (UI logic only)
│ │ ├── model.go # Main state machine, Update/View
│ │ ├── install_wizard.go # Installation wizard
│ │ ├── status.go # Server status polling
│ │ ├── commands.go # TUI command wrappers
│ │ └── version.go # Version information
│ └── hytale/ # Backend layer (game operations)
│ ├── bootstrap.go # Server installation/setup
│ ├── servers.go # Server lifecycle management
│ ├── tmux.go # Tmux session management
│ ├── update.go # Game/plugin updates
│ └── consts.go # Defaults and constants
├── tools/ # Helper scripts
│ ├── release.sh # GitHub release script
│ └── start.sh # Development build script
├── data/ # Server data (worlds, configs, logs)
├── docs/ # Documentation
└── install.sh # Global installation script (build from source)
Note: There is no scripts/ directory. Authentication and server scripts are handled via external tools (hytale-downloader, hytale-auth).
Technology Stack¶
Core Dependencies¶
- Bubble Tea (
github.com/charmbracelet/bubbletea) - TUI framework - Bubbles (
github.com/charmbracelet/bubbles) - Reusable TUI components: viewport- Scrollable content displaytextinput- Text input fieldsspinner- Loading animationsprogress- Progress bars- Lip Gloss (
github.com/charmbracelet/lipgloss) - Styling and colors
Architecture Patterns¶
1. State Machine Pattern (model.go)¶
The TUI uses a model struct that holds all state and implements Bubble Tea's Model interface:
type model struct {
view viewMode // Current view (main menu, wizard, etc.)
tab tab // Current tab (Install, Updates, Servers, Tools)
items []menuItem // Menu items for current tab
cursor int // Selected menu item
status string // Status bar message
running bool // Is a command running?
wizard installWizard // Install wizard state
viewport viewport.Model // Viewport for logs/status
serverStatuses []serverStatus // Server status tracking
// ... other state
}
Key Methods:
- Init() tea.Cmd - Initialize and return initial commands
- Update(msg tea.Msg) (tea.Model, tea.Cmd) - Handle messages, return updated model + commands
- View() string - Render the current view as a string
2. View Modes¶
Use an enum for different views:
type viewMode int
const (
viewMain viewMode = iota
viewInstallWizard
viewViewport
// ... other views
)
3. Tabbed Menu System¶
Organize menu items into tabs:
- Install Tab: Installation wizard, dependencies
- Updates Tab: Game updates, plugin updates
- Servers Tab: Status dashboard, logs, start/stop/restart, scale up/down
- Tools Tab: Config editing, cleanup, utilities
4. Command Pattern¶
Long-running operations return commands via tea.Cmd:
func runBootstrapGo(cfg hytale.BootstrapConfig) tea.Cmd {
return func() tea.Msg {
ctx, cancel := context.WithCancel(context.Background())
SetInstallCancel(cancel)
defer CancelInstall()
out, err := hytale.BootstrapWithContext(ctx, cfg)
return commandFinishedMsg{
output: out,
err: err,
}
}
}
The Update() method handles these messages and updates the UI.
Backend Layer (src/internal/hytale/)¶
Bootstrap (bootstrap.go)¶
Main installation function that:
- Creates directory structure (data/Server/, data/universe/, etc.)
- Downloads/installs Hytale server files via hytale-downloader
- Configures server instances
- Writes configuration files (config.json, permissions.json, etc.)
Key Function:
Server Management (servers.go)¶
Functions for:
- AddServerInstanceWithContext() - Scale up servers
- RemoveLastServerInstance() - Scale down servers
- DetectNumServers() - Detection from existing configs
- GetServerJarPath() - Get server JAR path
Process Management (tmux.go)¶
Manages server processes via tmux:
- TmuxManager struct with server count detection
- StartAll(), StopAll(), RestartAll() - Bulk operations
- Status() - Human-readable status for all servers
- Logs() - Read server logs
- Session naming: hytale-server-1, hytale-server-2, etc.
Updates (update.go)¶
Handle game and plugin updates:
- Download latest Hytale server files via hytale-downloader
- Update plugins/addons
- Deploy to all servers
Constants (consts.go)¶
Defaults and constants:
const (
DefaultHytaleUser = "hytale"
DefaultNumServers = 1
DefaultBasePort = 5520
DefaultJVMArgs = "-Xms4G -Xmx8G"
TmuxSessionPrefix = "hytale-server-"
DataDirBase = "./data" // Relative to where hsm is run
ConfigDir = "./data"
// ...
)
TUI-Backend Integration¶
Commands in tui/commands.go¶
Wrap backend operations as TUI commands:
func runBootstrapGo(cfg hytale.BootstrapConfig) tea.Cmd {
return func() tea.Msg {
ctx, cancel := context.WithCancel(context.Background())
SetInstallCancel(cancel)
defer CancelInstall()
out, err := hytale.BootstrapWithContext(ctx, cfg)
return commandFinishedMsg{
output: out,
err: err,
}
}
}
Context Cancellation¶
Support cancellation for long operations:
- Store context.CancelFunc globally
- Call Cancel() on user interrupt (Ctrl+C in wizard)
- Backend operations check ctx.Done()
Status Polling¶
Server status is polled every 2 seconds:
func pollServerStatus() tea.Cmd {
return tea.Tick(2*time.Second, func(time.Time) tea.Msg {
numServers := hytale.DetectNumServers()
tm := hytale.NewTmuxManager(hytale.DefaultBasePort)
statuses := tm.Status(numServers)
return serverStatusMsg{statuses: statuses}
})
}
Hytale-Specific Adaptations¶
Config Files¶
Hytale uses:
- config.json - Main server configuration
- whitelist.json - Whitelist
- bans.json - Banned players
- permissions.json - Player permissions
All configs are stored in data/ directory.
Ports¶
Hytale typically uses: - Game port (default 5520 UDP) - Incrementing ports for multiple servers (5520, 5521, 5522, ...)
Note: Hytale uses QUIC over UDP (not TCP).
Process Management¶
Hytale server:
- Launched via Java: java -jar HytaleServer.jar
- Requires Java 25+ runtime
- Managed via tmux sessions
- JVM arguments configurable (-Xms4G -Xmx8G)
Update Process¶
Hytale:
- Download server files using hytale-downloader
- Uses OAuth authentication for downloads
- Files extracted to data/Server/ directory
- Assets stored in data/Assets.zip
Styling and UX¶
Colors and Styles¶
Use Charm Lip Gloss styles:
import "github.com/charmbracelet/lipgloss"
var (
titleStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("205"))
selectedStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("205"))
// ...
)
Key UX Features¶
- Progress Indicators
- Status messages in footer
-
Real-time status polling
-
Error Handling
- Red error messages in status bar
- Validation feedback on forms
-
Graceful error messages (not raw stack traces)
-
Responsive Design
- Adjust viewport height based on terminal size
- Scrollable wizard if fields exceed terminal height
-
Wrapping text in viewports
-
Keyboard Shortcuts
↑/↓orj/k- Navigate menu items←/→orh/l- Switch tabsEnter- Execute selected actionqorCtrl+C- QuitEsc- Back to main menu
Development Workflow¶
-
Make Changes
-
Build
-
Test
-
Verify
- TUI builds without errors
- Navigation works smoothly
- Commands execute correctly
- Status updates in real-time
Best Practices¶
- Keep TUI layer completely separate from game-specific logic
- Backend should be testable without TUI
- Use context for cancellation
- Log all operations for debugging
- Follow existing naming conventions
- Handle edge cases (no servers installed, permission errors, etc.)
- Support both interactive (TUI) and non-interactive (CLI) modes (future)
Testing Checklist¶
- Install wizard completes successfully
- Multiple servers can be created
- Servers can be started/stopped/restarted
- Status polling updates correctly
- Logs viewport displays correctly
- Status dashboard shows server states
- Server scaling (add/remove) works
- Config updates apply to all servers
- Game/plugin updates work
- Error handling is graceful
- Navigation (tabs, menus) is smooth
- Cancellation works for long operations
Key Files Reference¶
src/cmd/hytale-tui/main.go- Entry pointsrc/internal/tui/model.go- Main state machinesrc/internal/tui/install_wizard.go- Installation wizardsrc/internal/tui/commands.go- Command wrapperssrc/internal/tui/status.go- Status pollingsrc/internal/hytale/bootstrap.go- Installation logicsrc/internal/hytale/tmux.go- Process managementsrc/internal/hytale/servers.go- Server lifecycle