// package config manages launcher configuration and system paths. package config import ( "encoding/json" "fmt" "os" "path/filepath" "runtime" ) // AppName is the canonical name for the launcher and its data directory. const AppName = "MrixsCraft" // Settings represents the user-configurable launcher settings stored in launcher.json. type Settings struct { ServerURL string `json:"server_url"` // Base URL of the backend (e.g. "https://minecraft.mrixs.me") SelectedPack string `json:"selected_pack"` // Slug of the selected modpack MemoryMB int `json:"memory_mb"` // Allocated RAM in MB (for -Xms / -Xmx) ExtraArgs string `json:"extra_args"` // Additional JVM flags Width int `json:"window_width"` // Main window width Height int `json:"window_height"` // Main window height } // serverEnvURL is the environment variable that overrides the backend base URL. const serverEnvURL = "MRIXSCRAFT_SERVER_URL" // defaultServerURL is used when neither the env var nor launcher.json provide a value. const defaultServerURL = "https://minecraft.mrixs.me" // DefaultSettings returns sensible defaults. func DefaultSettings() Settings { url := os.Getenv(serverEnvURL) if url == "" { url = defaultServerURL } return Settings{ ServerURL: url, SelectedPack: "", MemoryMB: 4096, ExtraArgs: "", Width: 900, Height: 600, } } // RootDir returns the OS-specific data directory for the launcher. func RootDir() (string, error) { switch runtime.GOOS { case "windows": appData := os.Getenv("APPDATA") if appData == "" { return "", fmt.Errorf("APPDATA environment variable is empty") } return filepath.Join(appData, AppName), nil case "darwin": home, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("resolving home directory: %w", err) } return filepath.Join(home, "Library", "Application Support", AppName), nil default: // linux and other unixes home, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("resolving home directory: %w", err) } return filepath.Join(home, "."+AppName), nil } } // EnsureRoot creates the root data directory if it does not exist. func EnsureRoot() (string, error) { dir, err := RootDir() if err != nil { return "", err } if err := os.MkdirAll(dir, 0o755); err != nil { return "", fmt.Errorf("creating root directory %s: %w", dir, err) } return dir, nil } // Subdirectory returns (and optionally creates) a subdirectory under the root. func Subdirectory(name string, create bool) (string, error) { root, err := RootDir() if err != nil { return "", err } path := filepath.Join(root, name) if create { if err := os.MkdirAll(path, 0o755); err != nil { return "", fmt.Errorf("creating subdirectory %s: %w", path, err) } } return path, nil } // JavaDir returns the directory for a specific Java version. func JavaDir(version int) (string, error) { return Subdirectory(filepath.Join("Java", fmt.Sprintf("%d", version)), true) } // InstancesDir returns the directory containing modpack instances. func InstancesDir() (string, error) { return Subdirectory("instances", true) } // InstanceDir returns the directory for a specific modpack. func InstanceDir(slug string) (string, error) { return Subdirectory(filepath.Join("instances", slug), true) } // AssetsDir returns the assets cache directory. func AssetsDir() (string, error) { return Subdirectory("assets", true) } // LibrariesDir returns the libraries cache directory. func LibrariesDir() (string, error) { return Subdirectory("libraries", true) } // LauncherJSONPath returns the full path to launcher.json. func LauncherJSONPath() (string, error) { root, err := RootDir() if err != nil { return "", err } return filepath.Join(root, "launcher.json"), nil } // Load reads launcher.json from disk, returning defaults if the file does not exist. func Load() (Settings, error) { path, err := LauncherJSONPath() if err != nil { return Settings{}, err } data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return DefaultSettings(), nil } return Settings{}, fmt.Errorf("reading %s: %w", path, err) } var s Settings if err := json.Unmarshal(data, &s); err != nil { return Settings{}, fmt.Errorf("parsing %s: %w", path, err) } return s, nil } // Save writes the settings to launcher.json. func Save(s Settings) error { path, err := LauncherJSONPath() if err != nil { return err } data, err := json.MarshalIndent(s, "", " ") if err != nil { return fmt.Errorf("serializing settings: %w", err) } if err := os.WriteFile(path, data, 0o600); err != nil { return fmt.Errorf("writing %s: %w", path, err) } return nil }