The last version the About popup was shown for is saved as last_seen_version in settings.cfg. When the game starts with another version, About opens to show what is new.
227 lines
9.1 KiB
GDScript
227 lines
9.1 KiB
GDScript
extends Node
|
|
## The game settings and the players, shared by the whole game.
|
|
##
|
|
## Registered as the [code]Settings[/code] autoload.
|
|
|
|
const MAX_NUMBER_OF_PLAYERS: int = 7
|
|
## How many letters a search needs before it also finds names with the letters in order
|
|
## but apart, e.g. "lttp" for "A Link to the Past". Shorter searches only find names
|
|
## that contain the text, since a letter or two in order matches most of the library.
|
|
const LETTERS_FOR_LOOSE_SEARCH: int = 3
|
|
## The MusicServers that can always be picked in the settings.
|
|
const SERVERS: Array[String] = [
|
|
"https://music.sanplex.xyz", "https://tmusic.sanplex.xyz", "https://rmusic.sanplex.xyz",
|
|
"https://old-music.sanplex.xyz", "http://localhost:8080",
|
|
]
|
|
## The server debug mode uses.
|
|
const DEBUG_SERVER: String = "http://localhost:8080"
|
|
## The winning score debug mode starts with, so a game is quick to win while testing.
|
|
const DEBUG_WINNING_SCORE: int = 5
|
|
## The settings the game saves by itself, by section, in [method saved_settings_path].
|
|
## Count plays in statistics and Prefer less played soundtracks are left out on purpose.
|
|
const SAVED_SETTINGS: Dictionary[String, Array] = {
|
|
"quiz": [
|
|
&"auto_repeat_song", &"stop_after_current", &"hide_next_track", &"hide_beginning",
|
|
&"hide_for_seconds", &"hide_length", &"hide_ticks", &"winning_score",
|
|
&"inspiration_list_speed",
|
|
],
|
|
"window": [&"fullscreen"],
|
|
"app": [&"last_seen_version"],
|
|
}
|
|
## When this environment variable is set, the settings files are kept in that folder
|
|
## instead of the user's config folder. The tests set it so they never touch the real files.
|
|
const CONFIG_DIR_ENV: String = "MUSICPLAYER_CONFIG_DIR"
|
|
## The file the user edits by hand. The game writes it only when it is missing, with every
|
|
## setting commented out.
|
|
const CONFIG_FILE_NAME: String = "config.ini"
|
|
const CONFIG_TEMPLATE: String = """# MusicPlayer config. The game never changes this file, so edit it as you like.
|
|
# Lines starting with # or ; are comments. Remove the # in front of a setting to use it,
|
|
# and put it back to go back to the default.
|
|
|
|
[server]
|
|
# The MusicServer to use when the game starts. Another one can still be picked in
|
|
# Settings for as long as the game runs. Debug mode uses http://localhost:8080.
|
|
#url = https://music.sanplex.xyz
|
|
|
|
# Password for getting an API token from the server.
|
|
#password =
|
|
|
|
[game]
|
|
# true or false. Debug mode uses the local server, starts with a winning score of 5 and
|
|
# shows DEBUG in the top bar. Without this setting, the game is in debug mode when run
|
|
# from the Godot editor and not when exported.
|
|
#debug = true
|
|
"""
|
|
|
|
var default_path: String = "https://music.sanplex.xyz"
|
|
## Password for getting an API token from the server. Any password works when the
|
|
## server has none set.
|
|
var api_password: String = ""
|
|
## The servers that can be picked in Settings: [constant SERVERS], plus the server from
|
|
## the config file when it is not one of them.
|
|
var servers: Array[String] = SERVERS.duplicate()
|
|
var selected_server: int = 0
|
|
var is_local: bool = false
|
|
var is_debug: bool = false
|
|
var stop_after_current: bool = true
|
|
var auto_repeat_song: bool = true
|
|
var hide_next_track: bool = true
|
|
var add_to_stats: bool = true
|
|
var use_low_played_mode: bool = false
|
|
var winning_score: int = 20
|
|
var fullscreen: bool = false
|
|
var play_local: bool = false
|
|
var inspiration_list_speed: int = 1
|
|
var hide_for_seconds: float = 5.0
|
|
var hide_beginning: bool = false
|
|
var hide_length: bool = false
|
|
var hide_ticks: bool = false
|
|
var player_array: Array[PlayerObject]
|
|
var edit_players: bool = false
|
|
var currently_syncing: bool = false
|
|
var character_select_open: bool = false
|
|
## The ID of the game's play queue on each server, by server URL, set by
|
|
## [code]MusicServerClient[/code]. Saved in the [code][queues][/code] section of
|
|
## [method saved_settings_path], so the game goes on with the same queue after a restart.
|
|
var queue_ids: Dictionary[String, String] = {}
|
|
## The version the game last showed the About popup for. When it is not [member version],
|
|
## this version is new and the main window opens the About popup to show what is new.
|
|
var last_seen_version: String = ""
|
|
|
|
## The game version, set in Project Settings > Application > Config > Version.
|
|
## Releases are made from it, see RELEASING.md.
|
|
var version: String = ProjectSettings.get_setting("application/config/version", "")
|
|
|
|
|
|
# Autoloads are ready before any scene, so the server and debug mode from the config file
|
|
# are used from the very first request. The saved settings come after, so the debug
|
|
# winning score is only used until another one is saved.
|
|
func _ready() -> void:
|
|
apply_config(load_config())
|
|
load_saved_settings()
|
|
if fullscreen:
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
|
|
|
|
|
## Uses the server, password and debug mode from the hand-edited [param config]. Settings
|
|
## left out of it get their defaults.
|
|
func apply_config(config: IniFile) -> void:
|
|
is_debug = config.get_bool("game", "debug", OS.has_feature("debug"))
|
|
default_path = DEBUG_SERVER if is_debug else SERVERS[0]
|
|
if is_debug:
|
|
winning_score = DEBUG_WINNING_SCORE
|
|
var url: String = config.get_string("server", "url")
|
|
if !url.is_empty():
|
|
default_path = url.trim_suffix("/")
|
|
if !servers.has(default_path):
|
|
servers.append(default_path)
|
|
selected_server = servers.find(default_path)
|
|
api_password = config.get_string("server", "password")
|
|
|
|
|
|
## The folder with the settings files: a MusicPlayer folder in the user's config folder,
|
|
## e.g. [code]~/.config[/code] on Linux and [code]%APPDATA%[/code] on Windows.
|
|
func config_dir() -> String:
|
|
var dir: String = OS.get_environment(CONFIG_DIR_ENV)
|
|
if dir.is_empty():
|
|
dir = OS.get_config_dir().path_join("MusicPlayer")
|
|
return dir
|
|
|
|
|
|
## Where the hand-edited config file is.
|
|
func config_path() -> String:
|
|
return config_dir().path_join(CONFIG_FILE_NAME)
|
|
|
|
|
|
## Where the game saves its settings. Debug mode uses a file of its own, so testing never
|
|
## changes the real settings.
|
|
func saved_settings_path() -> String:
|
|
var file_name: String = "settings_debug.cfg" if is_debug else "settings.cfg"
|
|
return config_dir().path_join(file_name)
|
|
|
|
|
|
## Reads the hand-edited config file. When it is missing, writes one with every setting
|
|
## commented out, so there is a file to edit.
|
|
func load_config() -> IniFile:
|
|
var path: String = config_path()
|
|
if !FileAccess.file_exists(path):
|
|
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
|
|
var template: FileAccess = FileAccess.open(path, FileAccess.WRITE)
|
|
if template != null:
|
|
template.store_string(CONFIG_TEMPLATE)
|
|
return IniFile.load_file(path)
|
|
|
|
|
|
## Loads the settings the game saved. Settings missing from the file, or saved with the
|
|
## wrong type, keep their current value.
|
|
func load_saved_settings() -> void:
|
|
var file: ConfigFile = ConfigFile.new()
|
|
if file.load(saved_settings_path()) != OK:
|
|
return
|
|
for section: String in SAVED_SETTINGS:
|
|
for setting: StringName in SAVED_SETTINGS[section]:
|
|
set(setting, _saved_value(file, section, setting, get(setting)))
|
|
if file.has_section("queues"):
|
|
queue_ids.clear()
|
|
for server: String in file.get_section_keys("queues"):
|
|
var id: Variant = file.get_value("queues", server)
|
|
if id is String:
|
|
queue_ids[server] = id
|
|
|
|
|
|
## Saves the settings in [constant SAVED_SETTINGS], so they are the same the next time
|
|
## the game starts.
|
|
func save_settings() -> void:
|
|
var path: String = saved_settings_path()
|
|
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
|
|
var file: ConfigFile = ConfigFile.new()
|
|
for section: String in SAVED_SETTINGS:
|
|
for setting: StringName in SAVED_SETTINGS[section]:
|
|
file.set_value(section, setting, get(setting))
|
|
for server: String in queue_ids:
|
|
file.set_value("queues", server, queue_ids[server])
|
|
var error: Error = file.save(path)
|
|
if error != OK:
|
|
GameLog.warning("Could not save the settings to %s: %s" % [path, error_string(error)])
|
|
|
|
|
|
## Sets the play queue ID for [param server] and saves it. Only the ID is changed in
|
|
## the file; the other settings in it stay as they were saved.
|
|
func set_queue_id(server: String, id: String) -> void:
|
|
queue_ids[server] = id
|
|
var path: String = saved_settings_path()
|
|
var file: ConfigFile = ConfigFile.new()
|
|
# A missing file is fine: it is written with only the queue ID
|
|
file.load(path)
|
|
file.set_value("queues", server, id)
|
|
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
|
|
var error: Error = file.save(path)
|
|
if error != OK:
|
|
GameLog.warning("Could not save the queue ID to %s: %s" % [path, error_string(error)])
|
|
|
|
|
|
## Turns fullscreen on or off and saves it.
|
|
func set_fullscreen(on: bool) -> void:
|
|
fullscreen = on
|
|
if on:
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
|
else:
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
|
save_settings()
|
|
|
|
|
|
# The saved value of [param key], or [param default] when it is missing or of another type
|
|
func _saved_value(file: ConfigFile, section: String, key: String, default: Variant) -> Variant:
|
|
var value: Variant = file.get_value(section, key, default)
|
|
if typeof(value) != typeof(default):
|
|
push_warning("%s in [%s] should be a %s, not %s" % [key, section,
|
|
type_string(typeof(default)), var_to_str(value)])
|
|
return default
|
|
return value
|
|
|
|
|
|
func delete_children(node: Node) -> void:
|
|
for n: Node in node.get_children():
|
|
node.remove_child(n)
|
|
n.queue_free()
|