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
viewActionResult
viewEditServerConfigs
viewServerStatus
// ... other views
)
Key Views:
- viewMain: Main menu with tabs and menu items (tab bar visible)
- viewInstallWizard: Interactive installation wizard with form fields
- viewViewport: Scrollable viewport for logs and other content
- viewServerStatus: Real-time server status dashboard with table view
- viewEditServerConfigs: Server configuration editor (future)
3. Tabbed Menu System¶
Organize menu items into tabs:
- Install Tab: Installation wizard with interactive form
- Updates Tab: Game updates, plugin updates, auto-update monitor
- Servers Tab: Start/stop/restart all servers, view logs, scale up/down
- Tools Tab: Edit server configs, view detailed server status dashboard
Each tab has its own menu items that execute specific actions. The tab bar is only visible in the main view (viewMain).
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