package control import ( "dk/internal/store" "fmt" "sync" ) // Control represents the game control settings 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"` } func (c *Control) Save() error { controlStore := GetStore() controlStore.UpdateControl(c) return nil } func (c *Control) Delete() error { controlStore := GetStore() controlStore.RemoveControl(c.ID) return nil } // Creates a new Control with sensible defaults func New() *Control { return &Control{ WorldSize: 200, // Default world size Open: 1, // Default open for registration AdminEmail: "", // No admin email by default Class1Name: "Mage", // Default class names Class2Name: "Warrior", Class3Name: "Paladin", } } // Validate checks if control has valid values func (c *Control) Validate() error { if c.WorldSize <= 0 || c.WorldSize > 10000 { return fmt.Errorf("control WorldSize must be between 1 and 10000") } if c.Open != 0 && c.Open != 1 { return fmt.Errorf("control Open must be 0 or 1") } return nil } // ControlStore provides in-memory storage for control settings type ControlStore struct { *store.BaseStore[Control] // Embedded generic store allByID []int // All IDs sorted by ID mu sync.RWMutex // Protects indices } // Global in-memory store var controlStore *ControlStore var storeOnce sync.Once // Initialize the in-memory store func initStore() { controlStore = &ControlStore{ BaseStore: store.NewBaseStore[Control](), allByID: make([]int, 0), } } // GetStore returns the global control store func GetStore() *ControlStore { storeOnce.Do(initStore) return controlStore } // AddControl adds a control to the in-memory store and updates all indices func (cs *ControlStore) AddControl(control *Control) { cs.mu.Lock() defer cs.mu.Unlock() // Validate control if err := control.Validate(); err != nil { return } // Add to base store cs.Add(control.ID, control) // Rebuild indices cs.rebuildIndicesUnsafe() } // RemoveControl removes a control from the store and updates indices func (cs *ControlStore) RemoveControl(id int) { cs.mu.Lock() defer cs.mu.Unlock() // Remove from base store cs.Remove(id) // Rebuild indices cs.rebuildIndicesUnsafe() } // UpdateControl updates a control efficiently func (cs *ControlStore) UpdateControl(control *Control) { cs.mu.Lock() defer cs.mu.Unlock() // Validate control if err := control.Validate(); err != nil { return } // Update base store cs.Add(control.ID, control) // Rebuild indices cs.rebuildIndicesUnsafe() } // LoadData loads control data from JSON file, or starts with empty store func LoadData(dataPath string) error { cs := GetStore() // Load from base store, which handles JSON loading if err := cs.BaseStore.LoadData(dataPath); err != nil { return err } // Rebuild indices from loaded data cs.rebuildIndices() return nil } // SaveData saves control data to JSON file func SaveData(dataPath string) error { cs := GetStore() return cs.BaseStore.SaveData(dataPath) } // rebuildIndicesUnsafe rebuilds all indices from base store data (caller must hold lock) func (cs *ControlStore) rebuildIndicesUnsafe() { // Clear indices cs.allByID = make([]int, 0) // Collect all controls allControls := cs.GetAll() for id := range allControls { cs.allByID = append(cs.allByID, id) } // Sort by ID (though typically only one control record exists) for i := 0; i < len(cs.allByID); i++ { for j := i + 1; j < len(cs.allByID); j++ { if cs.allByID[i] > cs.allByID[j] { cs.allByID[i], cs.allByID[j] = cs.allByID[j], cs.allByID[i] } } } } // rebuildIndices rebuilds all control-specific indices from base store data func (cs *ControlStore) rebuildIndices() { cs.mu.Lock() defer cs.mu.Unlock() cs.rebuildIndicesUnsafe() } // Retrieves the control record by ID (typically only ID 1 exists) func Find(id int) (*Control, error) { cs := GetStore() control, exists := cs.GetByID(id) if !exists { return nil, fmt.Errorf("control with ID %d not found", id) } return control, nil } // Retrieves the main control record (ID 1) func Get() (*Control, error) { return Find(1) } // Saves a new control to the in-memory store and sets the ID func (c *Control) Insert() error { cs := GetStore() // Validate before insertion if err := c.Validate(); err != nil { return fmt.Errorf("validation failed: %w", err) } // Assign new ID if not set if c.ID == 0 { c.ID = cs.GetNextID() } // Add to store cs.AddControl(c) return nil } // Returns true if the game world is open for new players func (c *Control) IsOpen() bool { return c.Open == 1 } // 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 } } // Closes the game world to new players func (c *Control) Close() { c.Open = 0 } // Opens the game world to new players func (c *Control) OpenWorld() { c.Open = 1 } // 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 } // 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] } } // 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 "" } } // 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 } } // 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 } // 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 } // Returns true if an admin email is configured func (c *Control) HasAdminEmail() bool { return c.AdminEmail != "" } // Returns true if the world size is within reasonable bounds func (c *Control) IsWorldSizeValid() bool { return c.WorldSize > 0 && c.WorldSize <= 10000 } // Returns the world radius (half the world size) func (c *Control) GetWorldRadius() int { return c.WorldSize / 2 } // 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 } // 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 }