doodle/pkg/usercfg/usercfg.go

140 lines
3.4 KiB
Go

/*
Package usercfg has functions around the user's Game Settings.
Other places in the codebase to look for its related functionality:
- pkg/windows/settings.go: the Settings Window is the UI owner of
this feature, it adjusts the usercfg.Current struct and Saves the
changes to disk.
*/
package usercfg
import (
"bytes"
"crypto/rand"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"time"
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
"git.kirsle.net/go/render"
)
// Settings are the available game settings.
type Settings struct {
// Initialized is set true the first time the settings are saved to
// disk, so the game may decide some default settings for first-time
// user experience, e.g. set horizontal toolbars for mobile.
Initialized bool
Entropy []byte `json:"entropy"`
// Configurable settings (pkg/windows/settings.go)
HorizontalToolbars bool `json:",omitempty"`
EnableFeatures bool `json:",omitempty"`
CrosshairSize int `json:",omitempty"`
CrosshairColor render.Color
HideTouchHints bool `json:",omitempty"`
DisableAutosave bool `json:",omitempty"`
ControllerStyle int
// Secret boolprops from balance/boolprops.go
ShowHiddenDoodads bool `json:",omitempty"`
WriteLockOverride bool `json:",omitempty"`
JSONIndent bool `json:",omitempty"`
EnableCheatsMenu bool `json:",omitempty"`
// Bookkeeping.
UpdatedAt time.Time
}
// Current loaded settings, good defaults by default.
var Current = Defaults()
// Defaults returns sensible default user settings.
func Defaults() *Settings {
settings := &Settings{}
// If not a touchscreen device, disable touchscreen hints as default.
if !native.HasTouchscreen(shmem.CurrentRenderEngine) {
settings.HideTouchHints = true
}
return settings
}
// Filepath returns the path to the settings file.
func Filepath() string {
return filepath.Join(userdir.ProfileDirectory, "settings.json")
}
// Save the settings to disk.
func Save() error {
var (
filename = Filepath()
bin = bytes.NewBuffer([]byte{})
enc = json.NewEncoder(bin)
)
enc.SetIndent("", "\t")
Current.Initialized = true
Current.UpdatedAt = time.Now()
if Current.Entropy == nil || len(Current.Entropy) == 0 {
if key, err := MakeEntropy(); err == nil {
Current.Entropy = key
}
}
if err := enc.Encode(Current); err != nil {
return err
}
err := ioutil.WriteFile(filename, bin.Bytes(), 0644)
return err
}
// Load the settings from disk. The loaded settings will be available
// at usercfg.Current.
func Load() error {
var (
filename = Filepath()
settings = Defaults()
)
if _, err := os.Stat(filename); os.IsNotExist(err) {
Current = settings
return nil // no file, no problem
}
fh, err := os.Open(filename)
if err != nil {
return err
}
// Decode JSON from file.
dec := json.NewDecoder(fh)
err = dec.Decode(settings)
if err != nil {
return err
}
Current = settings
// If we don't have an entropy key saved, make one and save it.
if Current.Entropy == nil || len(Current.Entropy) == 0 {
Save()
}
return nil
}
// MakeEntropy creates a random string one time that saves into the settings.json,
// used for checksum calculations for the user's savegame.
func MakeEntropy() ([]byte, error) {
key := make([]byte, 16)
_, err := rand.Read(key)
if err != nil {
return nil, err
}
return key, nil
}