Skip to content

Config JSON #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1b11698
config jsons (`get_mod_config`) + misc comment improvements
ithinkandicode Jan 15, 2023
dfa486a
README: convert indents to tabs
ithinkandicode Jan 15, 2023
846eb82
README: missing backticks in last commit
ithinkandicode Jan 15, 2023
465f0ec
README: remove "Notes on meta.json"
ithinkandicode Jan 15, 2023
6210d00
config jsons - support for defaults
ithinkandicode Jan 15, 2023
858ecb4
Config JSONs - implement defaults in manifest.json (`config_defaults`)
ithinkandicode Jan 15, 2023
29c0c83
Add required key to manifest: `compatible_modloader_version`
ithinkandicode Jan 15, 2023
db3a6a9
README - add required manifest key `compatible_modloader_version`
ithinkandicode Jan 15, 2023
e41d736
README - fix `version` being listed as `version_number`
ithinkandicode Jan 15, 2023
3b6f4ef
add required key: `namespace`
ithinkandicode Jan 15, 2023
ec63c3d
README - spaces to tabs
ithinkandicode Jan 15, 2023
6c2c379
README - revert spaces to tabs for file trees
ithinkandicode Jan 15, 2023
4362c60
README - minor text tweak
ithinkandicode Jan 15, 2023
7aecf8d
Add resource path logging for `add_translation_from_resource`
ithinkandicode Jan 15, 2023
d9f133f
Merge remote-tracking branch 'upstream/main' into config-json
ithinkandicode Jan 15, 2023
34b16c8
Add docs for CLI args, and rename `mod-dev` to `log-dev`
ithinkandicode Jan 15, 2023
4c3def2
README - improve docs about JSON configs
ithinkandicode Jan 15, 2023
b20bfb2
README - add mod_log + dev_log
ithinkandicode Jan 15, 2023
67477a1
README - improve config error docs
ithinkandicode Jan 15, 2023
ca220f1
README - TOC
ithinkandicode Jan 15, 2023
8639567
README - table overview of helper methods
ithinkandicode Jan 15, 2023
e67e2cb
README - fix TOC links
ithinkandicode Jan 15, 2023
8e501b2
snake case for `game_install_directory`
ithinkandicode Jan 15, 2023
d428f79
Merge remote-tracking branch 'upstream/main' into config-json
ithinkandicode Jan 16, 2023
6b10be3
revert changes to line order/spacing, in `save_scene`
ithinkandicode Jan 16, 2023
335f628
Merge remote-tracking branch 'upstream/main' into config-json
ithinkandicode Jan 16, 2023
68f70bb
show loaded configs count in `_load_mod_configs`
ithinkandicode Jan 16, 2023
5323c2a
Merge remote-tracking branch 'upstream/main' into config-json
ithinkandicode Jan 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ Mods you create must have the following 2 files:
```json
{
"name": "ModName",
"version": "1.0.0",
"namespace": "AuthorName",
"version_number": "1.0.0",
"description": "Mod description goes here",
"website_url": "https://github.com/example/repo",
"dependencies": [
Expand All @@ -56,6 +57,7 @@ Mods you create must have the following 2 files:
"authors": ["AuthorName"],
"compatible_mod_loader_version": "3.0.0",
"compatible_game_version": ["0.6.1.6"],
"config_defaults": {}
}
}
}
Expand Down
129 changes: 128 additions & 1 deletion loader/mod_loader.gd
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const REQUIRED_MANIFEST_KEYS_EXTRA = [
"authors",
"compatible_mod_loader_version",
"compatible_game_version",
"config_defaults",
]

# Set to true to require using "--enable-mods" to enable them
Expand All @@ -91,6 +92,12 @@ var mod_load_order = []
# Example: --mods-path="C://path/mods"
var os_mods_path_override = ""

# Override for the path config JSONs are loaded from
# Default: "res://configs"
# Set via: --configs-path
# Example: --configs-path="C://path/configs"
var os_configs_path_override = ""

# Any mods that are missing their dependancies are added to this
# Example property: "mod_id": ["dep_mod_id_0", "dep_mod_id_2"]
var mod_missing_dependencies = {}
Expand All @@ -116,6 +123,12 @@ func _init():
os_mods_path_override = cmd_line_mod_path
mod_log("The path mods are loaded from has been changed via the CLI arg `--mods-path`, to: " + cmd_line_mod_path, LOG_NAME)

# Check for the CLI arg that overrides the configs path
var cmd_line_configs_path = _get_cmd_line_arg("--configs-path")
if cmd_line_configs_path != "":
os_configs_path_override = cmd_line_configs_path
mod_log("The path configs are loaded from has been changed via the CLI arg `--configs-path`, to: " + cmd_line_configs_path, LOG_NAME)

# Loop over "res://mods" and add any mod zips to the unpacked virtual
# directory (UNPACKED_DIR)
_load_mod_zips()
Expand All @@ -125,6 +138,10 @@ func _init():
# directory, which adds their data to mod_data.
_setup_mods()

# Set up mod configs. If a mod's JSON file is found, its data gets added
# to mod_data.{mod_id}.config
_load_mod_configs()

# Loop over all loaded mods via their entry in mod_data. Verify that they
# have all the required files (REQUIRED_MOD_FILES), load their meta data
# (from their manifest.json file), and verify that the meta JSON has all
Expand Down Expand Up @@ -317,6 +334,53 @@ func _setup_mods():
dir.list_dir_end()


# Load mod config JSONs from res://configs
func _load_mod_configs():
var found_configs_count = 0
var configs_path = _get_local_folder_dir("configs")

# CLI override, set with `--configs-path="C://path/configs"`
# (similar to os_mods_path_override)
if (os_configs_path_override != ""):
configs_path = os_configs_path_override

for mod_id in mod_data:
var json_path = configs_path.plus_file(mod_id + ".json")
var mod_config = _get_json_as_dict(json_path)

dev_log(str("Config JSON: Looking for config at path: ", json_path), LOG_NAME)

if mod_config.size() > 0:
found_configs_count += 1

mod_log(str("Config JSON: Found a config file: '", json_path, "'"), LOG_NAME)
dev_log(str("Config JSON: File data: ", JSON.print(mod_config)), LOG_NAME)

# Check `load_from` option. This lets you specify the name of a
# different JSON file to load your config from. Must be in the same
# dir. Means you can have multiple config files for a single mod
# and switch between them quickly. Should include ".json" extension.
# Ignored if the filename matches the mod ID, or is empty
if mod_config.has("load_from"):
var new_path = mod_config.load_from
if new_path != "" && new_path != str(mod_id, ".json"):
mod_log(str("Config JSON: Following load_from path: ", new_path), LOG_NAME)
var new_config = _get_json_as_dict(configs_path + new_path)
if new_config.size() > 0 != null:
mod_config = new_config
mod_log(str("Config JSON: Loaded from custom json: ", new_path), LOG_NAME)
dev_log(str("Config JSON: File data: ", JSON.print(mod_config)), LOG_NAME)
else:
mod_log(str("Config JSON: ERROR - Could not load data via `load_from` for ", mod_id, ", at path: ", new_path), LOG_NAME)

mod_data[mod_id].config = mod_config

if found_configs_count > 0:
mod_log(str("Config JSON: Loaded ", str(found_configs_count), " config(s)"), LOG_NAME)
else:
mod_log(str("Config JSON: No mod configs were found"), LOG_NAME)


# Add a mod's data to mod_data.
# The mod_folder_path is just the folder name that was added to UNPACKED_DIR,
# which depends on the name used in a given mod ZIP (eg "mods-unpacked/Folder-Name")
Expand All @@ -333,6 +397,7 @@ func _init_mod_data(mod_folder_path):
mod_data[mod_id].is_loadable = true
mod_data[mod_id].importance = 0
mod_data[mod_id].dir = local_mod_path
mod_data[mod_id].config = {} # updated in _load_mod_configs

if DEBUG_ENABLE_STORING_FILEPATHS:
# Get the mod file paths
Expand Down Expand Up @@ -622,7 +687,6 @@ func _get_flat_view_dict(p_dir = "res://", p_match = "", p_match_is_regex = fals
return data



# Helpers
# =============================================================================

Expand Down Expand Up @@ -692,3 +756,66 @@ func save_scene(modified_scene, scene_path:String):
packed_scene.take_over_path(scene_path)
dev_log(str("save_scene - taking over path - new path -> ", packed_scene.resource_path), LOG_NAME)
_saved_objects.append(packed_scene)


# Get the config data for a specific mod. Always returns a dictionary with two
# keys: `error` and `data`.
# Data (`data`) is either the full config, or data from a specific key if one was specified.
# Error (`error`) is 0 if there were no errors, or > 0 if the setting could not be retrieved:
# 0 = No errors
# 1 = Invalid mod ID
# 2 = No custom JSON. File probably does not exist. Defaults will be used if available
# 3 = No custom JSON, and key was invalid when trying to get the default from your manifest defaults (`extra.godot.config_defaults`)
# 4 = Invalid key, although config data does exists
func get_mod_config(mod_id:String = "", key:String = "")->Dictionary:
var error_num = 0
var error_msg = ""
var data = {}
var defaults = null

# Invalid mod ID
if !mod_data.has(mod_id):
error_num = 1
error_msg = str("ERROR - Mod ID was invalid: ", mod_id)

# Mod ID is valid
if error_num == 0:
var config_data = mod_data[mod_id].config
defaults = mod_data[mod_id].meta_data.extra.godot.config_defaults

# No custom JSON file
if config_data.size() == 0:
error_num = 2
error_msg = str("WARNING - No config file for ", mod_id, ".json. ")
if key == "":
data = defaults
error_msg += "Using defaults (extra.godot.config_defaults)"
else:
if defaults.has(key):
data = defaults[key]
error_msg += str("Using defaults for key '", key, "' (extra.godot.config_defaults.", key, ")")
else:
error_num = 3
# error_msg = str("WARNING - No config file for Invalid key '", key, "' for mod ID: ", mod_id)
error_msg += str("Requested key '", key, "' is not present in the defaults (extra.godot.config_defaults.", key, ")")

# JSON file exists
if error_num == 0:
if key == "":
data = config_data
else:
if config_data.has(key):
data = config_data[key]
else:
error_num = 4
error_msg = str("WARNING - Invalid key '", key, "' for mod ID: ", mod_id)

# Log if any errors occured
if error_num != 0:
dev_log(str("Config: ", error_msg), mod_id)

return {
"error": error_num,
"error_msg": error_msg,
"data": data,
}