create control package

This commit is contained in:
Sky Johnson 2025-08-08 23:46:12 -05:00
parent 906042a67e
commit 42e090b05f
3 changed files with 1037 additions and 0 deletions

203
internal/control/control.go Normal file
View File

@ -0,0 +1,203 @@
package control
import (
"fmt"
"dk/internal/database"
"zombiezen.com/go/sqlite"
)
// Control represents the game control settings in the database
// There is only ever one control record with ID 1
type Control struct {
ID int `json:"id"`
WorldSize int `json:"world_size"`
Open int `json:"open"`
AdminEmail string `json:"admin_email"`
Class1Name string `json:"class_1_name"`
Class2Name string `json:"class_2_name"`
Class3Name string `json:"class_3_name"`
db *database.DB
}
// Find retrieves the control record by ID (typically only ID 1 exists)
func Find(db *database.DB, id int) (*Control, error) {
control := &Control{db: db}
query := "SELECT id, world_size, open, admin_email, class_1_name, class_2_name, class_3_name FROM control WHERE id = ?"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
control.ID = stmt.ColumnInt(0)
control.WorldSize = stmt.ColumnInt(1)
control.Open = stmt.ColumnInt(2)
control.AdminEmail = stmt.ColumnText(3)
control.Class1Name = stmt.ColumnText(4)
control.Class2Name = stmt.ColumnText(5)
control.Class3Name = stmt.ColumnText(6)
return nil
}, id)
if err != nil {
return nil, fmt.Errorf("failed to find control: %w", err)
}
if control.ID == 0 {
return nil, fmt.Errorf("control with ID %d not found", id)
}
return control, nil
}
// Get retrieves the main control record (ID 1)
func Get(db *database.DB) (*Control, error) {
return Find(db, 1)
}
// Save updates the control record in the database
func (c *Control) Save() error {
if c.ID == 0 {
return fmt.Errorf("cannot save control without ID")
}
query := `UPDATE control SET world_size = ?, open = ?, admin_email = ?, class_1_name = ?, class_2_name = ?, class_3_name = ? WHERE id = ?`
return c.db.Exec(query, c.WorldSize, c.Open, c.AdminEmail, c.Class1Name, c.Class2Name, c.Class3Name, c.ID)
}
// IsOpen returns true if the game world is open for new players
func (c *Control) IsOpen() bool {
return c.Open == 1
}
// SetOpen sets whether the game world is open for new players
func (c *Control) SetOpen(open bool) {
if open {
c.Open = 1
} else {
c.Open = 0
}
}
// Close closes the game world to new players
func (c *Control) Close() {
c.Open = 0
}
// OpenWorld opens the game world to new players
func (c *Control) OpenWorld() {
c.Open = 1
}
// GetClassNames returns all class names as a slice
func (c *Control) GetClassNames() []string {
classes := make([]string, 0, 3)
if c.Class1Name != "" {
classes = append(classes, c.Class1Name)
}
if c.Class2Name != "" {
classes = append(classes, c.Class2Name)
}
if c.Class3Name != "" {
classes = append(classes, c.Class3Name)
}
return classes
}
// SetClassNames sets all class names from a slice
func (c *Control) SetClassNames(classes []string) {
// Reset all class names
c.Class1Name = ""
c.Class2Name = ""
c.Class3Name = ""
// Set provided class names
if len(classes) > 0 {
c.Class1Name = classes[0]
}
if len(classes) > 1 {
c.Class2Name = classes[1]
}
if len(classes) > 2 {
c.Class3Name = classes[2]
}
}
// GetClassName returns the name of a specific class (1-3)
func (c *Control) GetClassName(classNum int) string {
switch classNum {
case 1:
return c.Class1Name
case 2:
return c.Class2Name
case 3:
return c.Class3Name
default:
return ""
}
}
// SetClassName sets the name of a specific class (1-3)
func (c *Control) SetClassName(classNum int, name string) bool {
switch classNum {
case 1:
c.Class1Name = name
return true
case 2:
c.Class2Name = name
return true
case 3:
c.Class3Name = name
return true
default:
return false
}
}
// IsValidClassName returns true if the given name matches one of the configured classes
func (c *Control) IsValidClassName(name string) bool {
if name == "" {
return false
}
return name == c.Class1Name || name == c.Class2Name || name == c.Class3Name
}
// GetClassNumber returns the class number (1-3) for a given class name, or 0 if not found
func (c *Control) GetClassNumber(name string) int {
if name == c.Class1Name && name != "" {
return 1
}
if name == c.Class2Name && name != "" {
return 2
}
if name == c.Class3Name && name != "" {
return 3
}
return 0
}
// HasAdminEmail returns true if an admin email is configured
func (c *Control) HasAdminEmail() bool {
return c.AdminEmail != ""
}
// IsWorldSizeValid returns true if the world size is within reasonable bounds
func (c *Control) IsWorldSizeValid() bool {
return c.WorldSize > 0 && c.WorldSize <= 10000
}
// GetWorldRadius returns the world radius (half the world size)
func (c *Control) GetWorldRadius() int {
return c.WorldSize / 2
}
// IsWithinWorldBounds returns true if the given coordinates are within world bounds
func (c *Control) IsWithinWorldBounds(x, y int) bool {
radius := c.GetWorldRadius()
return x >= -radius && x <= radius && y >= -radius && y <= radius
}
// GetWorldBounds returns the minimum and maximum coordinates for the world
func (c *Control) GetWorldBounds() (minX, minY, maxX, maxY int) {
radius := c.GetWorldRadius()
return -radius, -radius, radius, radius
}

View File

@ -0,0 +1,418 @@
package control
import (
"os"
"testing"
"dk/internal/database"
)
func setupTestDB(t *testing.T) *database.DB {
testDB := "test_control.db"
t.Cleanup(func() {
os.Remove(testDB)
})
db, err := database.Open(testDB)
if err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
// Create control table
createTable := `CREATE TABLE control (
id INTEGER PRIMARY KEY AUTOINCREMENT,
world_size INTEGER NOT NULL DEFAULT 250,
open INTEGER NOT NULL DEFAULT 1,
admin_email TEXT NOT NULL DEFAULT '',
class_1_name TEXT NOT NULL DEFAULT '',
class_2_name TEXT NOT NULL DEFAULT '',
class_3_name TEXT NOT NULL DEFAULT ''
)`
if err := db.Exec(createTable); err != nil {
t.Fatalf("Failed to create control table: %v", err)
}
// Insert default control record
insertControl := `INSERT INTO control VALUES (1, 250, 1, 'admin@example.com', 'Mage', 'Warrior', 'Paladin')`
if err := db.Exec(insertControl); err != nil {
t.Fatalf("Failed to insert test control: %v", err)
}
return db
}
func TestFind(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test finding existing control record
control, err := Find(db, 1)
if err != nil {
t.Fatalf("Failed to find control: %v", err)
}
if control.ID != 1 {
t.Errorf("Expected ID 1, got %d", control.ID)
}
if control.WorldSize != 250 {
t.Errorf("Expected world size 250, got %d", control.WorldSize)
}
if control.Open != 1 {
t.Errorf("Expected open 1, got %d", control.Open)
}
if control.AdminEmail != "admin@example.com" {
t.Errorf("Expected admin email 'admin@example.com', got '%s'", control.AdminEmail)
}
if control.Class1Name != "Mage" {
t.Errorf("Expected class 1 name 'Mage', got '%s'", control.Class1Name)
}
if control.Class2Name != "Warrior" {
t.Errorf("Expected class 2 name 'Warrior', got '%s'", control.Class2Name)
}
if control.Class3Name != "Paladin" {
t.Errorf("Expected class 3 name 'Paladin', got '%s'", control.Class3Name)
}
// Test finding non-existent control record
_, err = Find(db, 999)
if err == nil {
t.Error("Expected error when finding non-existent control")
}
}
func TestGet(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
// Test getting main control record
control, err := Get(db)
if err != nil {
t.Fatalf("Failed to get control: %v", err)
}
if control.ID != 1 {
t.Errorf("Expected ID 1, got %d", control.ID)
}
if control.WorldSize != 250 {
t.Errorf("Expected world size 250, got %d", control.WorldSize)
}
}
func TestSave(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, err := Get(db)
if err != nil {
t.Fatalf("Failed to get control: %v", err)
}
// Modify control settings
control.WorldSize = 500
control.Open = 0
control.AdminEmail = "newadmin@example.com"
control.Class1Name = "Wizard"
control.Class2Name = "Knight"
control.Class3Name = "Cleric"
// Save changes
err = control.Save()
if err != nil {
t.Fatalf("Failed to save control: %v", err)
}
// Verify changes were saved
updatedControl, err := Get(db)
if err != nil {
t.Fatalf("Failed to get updated control: %v", err)
}
if updatedControl.WorldSize != 500 {
t.Errorf("Expected updated world size 500, got %d", updatedControl.WorldSize)
}
if updatedControl.Open != 0 {
t.Errorf("Expected updated open 0, got %d", updatedControl.Open)
}
if updatedControl.AdminEmail != "newadmin@example.com" {
t.Errorf("Expected updated admin email 'newadmin@example.com', got '%s'", updatedControl.AdminEmail)
}
if updatedControl.Class1Name != "Wizard" {
t.Errorf("Expected updated class 1 name 'Wizard', got '%s'", updatedControl.Class1Name)
}
if updatedControl.Class2Name != "Knight" {
t.Errorf("Expected updated class 2 name 'Knight', got '%s'", updatedControl.Class2Name)
}
if updatedControl.Class3Name != "Cleric" {
t.Errorf("Expected updated class 3 name 'Cleric', got '%s'", updatedControl.Class3Name)
}
}
func TestOpenMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, _ := Get(db)
// Test IsOpen
if !control.IsOpen() {
t.Error("Expected control to be open initially")
}
// Test SetOpen
control.SetOpen(false)
if control.IsOpen() {
t.Error("Expected control to be closed after SetOpen(false)")
}
control.SetOpen(true)
if !control.IsOpen() {
t.Error("Expected control to be open after SetOpen(true)")
}
// Test Close
control.Close()
if control.IsOpen() {
t.Error("Expected control to be closed after Close()")
}
// Test OpenWorld
control.OpenWorld()
if !control.IsOpen() {
t.Error("Expected control to be open after OpenWorld()")
}
}
func TestClassMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, _ := Get(db)
// Test GetClassNames
classNames := control.GetClassNames()
expectedNames := []string{"Mage", "Warrior", "Paladin"}
if len(classNames) != len(expectedNames) {
t.Errorf("Expected %d class names, got %d", len(expectedNames), len(classNames))
}
for i, expected := range expectedNames {
if i < len(classNames) && classNames[i] != expected {
t.Errorf("Expected class name '%s' at position %d, got '%s'", expected, i, classNames[i])
}
}
// Test SetClassNames
newClasses := []string{"Sorcerer", "Barbarian", "Monk"}
control.SetClassNames(newClasses)
if control.Class1Name != "Sorcerer" {
t.Errorf("Expected class 1 name 'Sorcerer', got '%s'", control.Class1Name)
}
if control.Class2Name != "Barbarian" {
t.Errorf("Expected class 2 name 'Barbarian', got '%s'", control.Class2Name)
}
if control.Class3Name != "Monk" {
t.Errorf("Expected class 3 name 'Monk', got '%s'", control.Class3Name)
}
// Test SetClassNames with fewer than 3 classes
twoClasses := []string{"Ranger", "Druid"}
control.SetClassNames(twoClasses)
if control.Class1Name != "Ranger" {
t.Errorf("Expected class 1 name 'Ranger', got '%s'", control.Class1Name)
}
if control.Class2Name != "Druid" {
t.Errorf("Expected class 2 name 'Druid', got '%s'", control.Class2Name)
}
if control.Class3Name != "" {
t.Errorf("Expected class 3 name to be empty, got '%s'", control.Class3Name)
}
// Test GetClassName
if control.GetClassName(1) != "Ranger" {
t.Errorf("Expected class 1 name 'Ranger', got '%s'", control.GetClassName(1))
}
if control.GetClassName(2) != "Druid" {
t.Errorf("Expected class 2 name 'Druid', got '%s'", control.GetClassName(2))
}
if control.GetClassName(3) != "" {
t.Errorf("Expected class 3 name to be empty, got '%s'", control.GetClassName(3))
}
if control.GetClassName(4) != "" {
t.Errorf("Expected invalid class number to return empty string, got '%s'", control.GetClassName(4))
}
// Test SetClassName
if !control.SetClassName(3, "Rogue") {
t.Error("Expected SetClassName(3, 'Rogue') to return true")
}
if control.Class3Name != "Rogue" {
t.Errorf("Expected class 3 name 'Rogue', got '%s'", control.Class3Name)
}
if control.SetClassName(4, "Invalid") {
t.Error("Expected SetClassName(4, 'Invalid') to return false")
}
// Test IsValidClassName
if !control.IsValidClassName("Ranger") {
t.Error("Expected 'Ranger' to be a valid class name")
}
if !control.IsValidClassName("Druid") {
t.Error("Expected 'Druid' to be a valid class name")
}
if !control.IsValidClassName("Rogue") {
t.Error("Expected 'Rogue' to be a valid class name")
}
if control.IsValidClassName("Bard") {
t.Error("Expected 'Bard' not to be a valid class name")
}
if control.IsValidClassName("") {
t.Error("Expected empty string not to be a valid class name")
}
// Test GetClassNumber
if control.GetClassNumber("Ranger") != 1 {
t.Errorf("Expected 'Ranger' to be class number 1, got %d", control.GetClassNumber("Ranger"))
}
if control.GetClassNumber("Druid") != 2 {
t.Errorf("Expected 'Druid' to be class number 2, got %d", control.GetClassNumber("Druid"))
}
if control.GetClassNumber("Rogue") != 3 {
t.Errorf("Expected 'Rogue' to be class number 3, got %d", control.GetClassNumber("Rogue"))
}
if control.GetClassNumber("Bard") != 0 {
t.Errorf("Expected 'Bard' to return class number 0, got %d", control.GetClassNumber("Bard"))
}
if control.GetClassNumber("") != 0 {
t.Errorf("Expected empty string to return class number 0, got %d", control.GetClassNumber(""))
}
}
func TestEmailMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, _ := Get(db)
// Test HasAdminEmail (should be true initially)
if !control.HasAdminEmail() {
t.Error("Expected control to have admin email initially")
}
// Test with empty email
control.AdminEmail = ""
if control.HasAdminEmail() {
t.Error("Expected control not to have admin email when empty")
}
}
func TestWorldSizeMethods(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, _ := Get(db)
// Test IsWorldSizeValid
if !control.IsWorldSizeValid() {
t.Error("Expected world size 250 to be valid")
}
control.WorldSize = 0
if control.IsWorldSizeValid() {
t.Error("Expected world size 0 to be invalid")
}
control.WorldSize = 10001
if control.IsWorldSizeValid() {
t.Error("Expected world size 10001 to be invalid")
}
control.WorldSize = 1000
if !control.IsWorldSizeValid() {
t.Error("Expected world size 1000 to be valid")
}
// Test GetWorldRadius
expectedRadius := 500
if control.GetWorldRadius() != expectedRadius {
t.Errorf("Expected world radius %d, got %d", expectedRadius, control.GetWorldRadius())
}
// Test IsWithinWorldBounds
if !control.IsWithinWorldBounds(0, 0) {
t.Error("Expected (0,0) to be within world bounds")
}
if !control.IsWithinWorldBounds(500, 500) {
t.Error("Expected (500,500) to be within world bounds")
}
if !control.IsWithinWorldBounds(-500, -500) {
t.Error("Expected (-500,-500) to be within world bounds")
}
if control.IsWithinWorldBounds(501, 0) {
t.Error("Expected (501,0) to be outside world bounds")
}
if control.IsWithinWorldBounds(0, 501) {
t.Error("Expected (0,501) to be outside world bounds")
}
if control.IsWithinWorldBounds(-501, 0) {
t.Error("Expected (-501,0) to be outside world bounds")
}
if control.IsWithinWorldBounds(0, -501) {
t.Error("Expected (0,-501) to be outside world bounds")
}
// Test GetWorldBounds
minX, minY, maxX, maxY := control.GetWorldBounds()
expectedMin, expectedMax := -500, 500
if minX != expectedMin {
t.Errorf("Expected minX %d, got %d", expectedMin, minX)
}
if minY != expectedMin {
t.Errorf("Expected minY %d, got %d", expectedMin, minY)
}
if maxX != expectedMax {
t.Errorf("Expected maxX %d, got %d", expectedMax, maxX)
}
if maxY != expectedMax {
t.Errorf("Expected maxY %d, got %d", expectedMax, maxY)
}
}
func TestEmptyClassHandling(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
control, _ := Get(db)
// Set some classes to empty
control.Class2Name = ""
control.Class3Name = ""
// Test GetClassNames with empty classes
classNames := control.GetClassNames()
expectedCount := 1 // Only Class1Name should be included
if len(classNames) != expectedCount {
t.Errorf("Expected %d non-empty class names, got %d", expectedCount, len(classNames))
}
if len(classNames) > 0 && classNames[0] != "Mage" {
t.Errorf("Expected first class name 'Mage', got '%s'", classNames[0])
}
// Test IsValidClassName with empty string
if control.IsValidClassName("") {
t.Error("Expected empty string not to be valid class name")
}
// Test GetClassNumber with empty class names
if control.GetClassNumber("Warrior") != 0 {
t.Error("Expected empty class 2 not to match 'Warrior'")
}
}

416
internal/control/doc.go Normal file
View File

@ -0,0 +1,416 @@
/*
Package control is the active record implementation for game control settings.
The control package manages global game configuration settings stored in a single database record (ID 1). Unlike other packages, this one focuses on updating the existing control record rather than creating new ones, as there is only ever one set of game control settings.
# Basic Usage
To retrieve the main control settings:
settings, err := control.Get(db)
if err != nil {
log.Fatal(err)
}
fmt.Printf("World size: %d, Game open: %v\n", settings.WorldSize, settings.IsOpen())
To find control settings by ID (typically always 1):
settings, err := control.Find(db, 1)
if err != nil {
log.Fatal(err)
}
# Updating Settings
Control settings can be modified and saved back to the database:
settings, _ := control.Get(db)
settings.WorldSize = 500
settings.SetOpen(false)
settings.AdminEmail = "newadmin@game.com"
err := settings.Save()
if err != nil {
log.Fatal(err)
}
# Database Schema
The control table has the following structure:
CREATE TABLE control (
id INTEGER PRIMARY KEY AUTOINCREMENT,
world_size INTEGER NOT NULL DEFAULT 250,
open INTEGER NOT NULL DEFAULT 1,
admin_email TEXT NOT NULL DEFAULT '',
class_1_name TEXT NOT NULL DEFAULT '',
class_2_name TEXT NOT NULL DEFAULT '',
class_3_name TEXT NOT NULL DEFAULT ''
)
Where:
- id: Unique identifier (typically always 1)
- world_size: The size of the game world (used for coordinate bounds)
- open: Whether the game is open for new player registration (1=open, 0=closed)
- admin_email: Administrator email address for notifications
- class_1_name, class_2_name, class_3_name: Names of the three player classes
# Game World Management
## World Size and Boundaries
The world size determines the coordinate boundaries for the game:
settings, _ := control.Get(db)
// Get world size and radius
fmt.Printf("World size: %d\n", settings.WorldSize)
fmt.Printf("World radius: %d\n", settings.GetWorldRadius())
// Check coordinate boundaries
minX, minY, maxX, maxY := settings.GetWorldBounds()
fmt.Printf("World bounds: (%d,%d) to (%d,%d)\n", minX, minY, maxX, maxY)
// Validate coordinates
if settings.IsWithinWorldBounds(playerX, playerY) {
fmt.Println("Player is within world boundaries")
}
The world is centered at (0,0) with coordinates ranging from -radius to +radius.
## World Size Validation
Ensure world size settings are reasonable:
settings, _ := control.Get(db)
if !settings.IsWorldSizeValid() {
fmt.Println("Warning: World size is invalid (must be 1-10000)")
settings.WorldSize = 250 // Reset to default
settings.Save()
}
# Player Registration Control
## Managing Game Access
Control whether new players can register:
settings, _ := control.Get(db)
// Check current status
if settings.IsOpen() {
fmt.Println("Game is open for new players")
} else {
fmt.Println("Game is closed to new players")
}
// Change registration status
settings.SetOpen(false) // Close registration
settings.Close() // Alternative method
settings.OpenWorld() // Reopen registration
settings.Save()
## Maintenance Mode
Temporarily close the game for maintenance:
func enterMaintenanceMode(db *database.DB) error {
settings, err := control.Get(db)
if err != nil {
return err
}
settings.Close()
return settings.Save()
}
func exitMaintenanceMode(db *database.DB) error {
settings, err := control.Get(db)
if err != nil {
return err
}
settings.OpenWorld()
return settings.Save()
}
# Player Class Management
## Class Configuration
Manage the three player classes available in the game:
settings, _ := control.Get(db)
// Get all configured class names
classes := settings.GetClassNames()
fmt.Printf("Available classes: %v\n", classes)
// Get specific class name
mageClass := settings.GetClassName(1)
fmt.Printf("Class 1: %s\n", mageClass)
// Set all class names at once
newClasses := []string{"Sorcerer", "Paladin", "Assassin"}
settings.SetClassNames(newClasses)
settings.Save()
## Individual Class Management
Manage classes individually:
settings, _ := control.Get(db)
// Set individual class names
settings.SetClassName(1, "Wizard")
settings.SetClassName(2, "Knight")
settings.SetClassName(3, "Rogue")
settings.Save()
// Validate class names
if settings.IsValidClassName("Wizard") {
fmt.Println("Wizard is a valid class")
}
// Get class number by name
classNum := settings.GetClassNumber("Knight")
fmt.Printf("Knight is class number: %d\n", classNum)
## Class System Integration
Use class settings for player creation and validation:
func validatePlayerClass(db *database.DB, className string) bool {
settings, err := control.Get(db)
if err != nil {
return false
}
return settings.IsValidClassName(className)
}
func getAvailableClasses(db *database.DB) []string {
settings, err := control.Get(db)
if err != nil {
return []string{}
}
return settings.GetClassNames()
}
func getClassID(db *database.DB, className string) int {
settings, err := control.Get(db)
if err != nil {
return 0
}
return settings.GetClassNumber(className)
}
# Administrative Features
## Admin Contact Information
Manage administrator contact information:
settings, _ := control.Get(db)
// Check if admin email is configured
if settings.HasAdminEmail() {
fmt.Printf("Admin contact: %s\n", settings.AdminEmail)
} else {
fmt.Println("No admin email configured")
settings.AdminEmail = "admin@mygame.com"
settings.Save()
}
## Configuration Validation
Validate all control settings:
func validateControlSettings(db *database.DB) []string {
settings, err := control.Get(db)
if err != nil {
return []string{"Failed to load control settings"}
}
var issues []string
// Check world size
if !settings.IsWorldSizeValid() {
issues = append(issues, "Invalid world size")
}
// Check admin email
if !settings.HasAdminEmail() {
issues = append(issues, "No admin email configured")
}
// Check class names
classes := settings.GetClassNames()
if len(classes) == 0 {
issues = append(issues, "No player classes configured")
}
return issues
}
# Game Logic Integration
## Coordinate Validation
Use control settings for game coordinate validation:
func validatePlayerMovement(db *database.DB, newX, newY int) bool {
settings, err := control.Get(db)
if err != nil {
return false
}
return settings.IsWithinWorldBounds(newX, newY)
}
## Registration System
Integrate with player registration:
func canRegisterNewPlayer(db *database.DB) bool {
settings, err := control.Get(db)
if err != nil {
return false
}
return settings.IsOpen()
}
func getRegistrationMessage(db *database.DB) string {
settings, err := control.Get(db)
if err != nil {
return "Unable to check registration status"
}
if settings.IsOpen() {
classes := settings.GetClassNames()
return fmt.Sprintf("Welcome! Choose from these classes: %v", classes)
} else {
return "Registration is currently closed"
}
}
# Configuration Management
## Backup and Restore
Backup control settings:
func backupControlSettings(db *database.DB) (*control.Control, error) {
return control.Get(db)
}
func restoreControlSettings(db *database.DB, backup *control.Control) error {
settings, err := control.Get(db)
if err != nil {
return err
}
settings.WorldSize = backup.WorldSize
settings.Open = backup.Open
settings.AdminEmail = backup.AdminEmail
settings.Class1Name = backup.Class1Name
settings.Class2Name = backup.Class2Name
settings.Class3Name = backup.Class3Name
return settings.Save()
}
## Default Settings
Reset to default configuration:
func resetToDefaults(db *database.DB) error {
settings, err := control.Get(db)
if err != nil {
return err
}
settings.WorldSize = 250
settings.SetOpen(true)
settings.AdminEmail = ""
settings.SetClassNames([]string{"Mage", "Warrior", "Paladin"})
return settings.Save()
}
# Performance Considerations
## Caching Settings
Since control settings rarely change, consider caching:
var controlCache *control.Control
var cacheTime time.Time
func getCachedControlSettings(db *database.DB) (*control.Control, error) {
// Cache for 5 minutes
if controlCache != nil && time.Since(cacheTime) < 5*time.Minute {
return controlCache, nil
}
settings, err := control.Get(db)
if err != nil {
return nil, err
}
controlCache = settings
cacheTime = time.Now()
return settings, nil
}
## Batch Updates
Update multiple settings efficiently:
func updateGameConfiguration(db *database.DB, worldSize int, isOpen bool,
adminEmail string, classes []string) error {
settings, err := control.Get(db)
if err != nil {
return err
}
// Update all settings
settings.WorldSize = worldSize
settings.SetOpen(isOpen)
settings.AdminEmail = adminEmail
settings.SetClassNames(classes)
// Single save operation
return settings.Save()
}
# Error Handling
Common error scenarios and handling:
settings, err := control.Get(db)
if err != nil {
// Handle database connection issues or missing control record
log.Printf("Failed to load control settings: %v", err)
return
}
// Validate before using
if !settings.IsWorldSizeValid() {
log.Println("Warning: Invalid world size detected")
// Could reset to default or reject changes
}
// Save with error handling
if err := settings.Save(); err != nil {
log.Printf("Failed to save control settings: %v", err)
// Could retry or alert administrator
}
The control package provides a centralized way to manage all global game settings through a single, persistent record that can be easily modified and validated.
*/
package control