extends RefCounted ## A dedicated server's preferences (`server.json`) and its reserved names ## (`names.json` alongside). Created with defaults on first run and meant to ## be edited by hand; command-line flags override single values for one run. ## See docs/MULTIPLAYER_PLAN.md ยง4. const DEFAULT_PATH := "user://server/server.json" const DEFAULTS := { "server_name": "Emberwild server", "motd": "Welcome!", "port": 24242, "max_players": 8, "password": "", "world": "server-world", "worldlet": "valley", "seed": 1337, "pvp": false, "raiding": false, "locks": "owner_and_friends", "death_penalty": "grave", "sleep": "all_online", "pause_when_empty": true, "day_length_minutes": 20, "wolf_grace_days": 3, "difficulty": "normal", "autosave_minutes": 2, "backups_kept": 10, "admins": [], "allowlist": [], "banned": [], } ## Settings sent to players when they join (never the password or the lists). const PUBLIC := ["server_name", "motd", "max_players", "worldlet", "pvp", "raiding", "locks", "death_penalty", "sleep", "pause_when_empty", "day_length_minutes", "wolf_grace_days", "difficulty"] var path := DEFAULT_PATH var data: Dictionary = {} ## what's in server.json (saved) var overrides: Dictionary = {} ## command-line flags for this run (never saved) ## Reserved names: lower-case name -> {"name": as typed, "id": player id}. var names: Dictionary = {} static func open(p: String = DEFAULT_PATH) -> RefCounted: var c = load("res://scripts/net/server_config.gd").new() c.path = p DirAccess.make_dir_recursive_absolute(p.get_base_dir()) var loaded: Variant = JSON.parse_string(FileAccess.get_file_as_string(p)) if FileAccess.file_exists(p) else null c.data = DEFAULTS.duplicate(true) if loaded is Dictionary: for k in loaded: c.data[k] = loaded[k] else: c.save() # first run: write the defaults so they can be edited var n: Variant = JSON.parse_string(FileAccess.get_file_as_string(c.names_path())) \ if FileAccess.file_exists(c.names_path()) else null if n is Dictionary: c.names = n return c func names_path() -> String: return path.get_base_dir().path_join("names.json") func save() -> void: _write(path, data) func save_names() -> void: _write(names_path(), names) static func _write(p: String, d: Dictionary) -> void: var tmp := p + ".tmp" var f := FileAccess.open(tmp, FileAccess.WRITE) if f == null: push_error("[server] couldn't write %s" % p) return f.store_string(JSON.stringify(d, "\t")) f.close() DirAccess.rename_absolute(tmp, p) ## A setting's value for this run (a flag beats the file). func v(k: String) -> Variant: return overrides.get(k, data.get(k)) func public_info() -> Dictionary: var out := {} for k in PUBLIC: out[k] = v(k) return out ## The player id holding `n`, or "" if it's free. func name_owner(n: String) -> String: return String(names.get(n.to_lower(), {}).get("id", "")) ## Gives `n` to `id`, releasing any other name that id held. func reserve_name(n: String, id: String) -> void: for k in names.keys(): if names[k].id == id and k != n.to_lower(): names.erase(k) names[n.to_lower()] = {"name": n, "id": id} save_names() func is_admin(id: String) -> bool: return id in data.admins func add_admin(id: String) -> void: if not id in data.admins: data.admins.append(id) save()