Skip to content

Conversation

@Qubus0
Copy link
Collaborator

@Qubus0 Qubus0 commented Oct 16, 2024

big pr with some suboptimal mixing of things to get them fixed for dome, sorry about those.

the core of the zip peeking pr is about having info about mod metadata before the mod is loaded by load_resource_pack. this lets us compare the game version (and other data, in following prs) from the zip with the one set by the game. if a version mismatch is detected the mod won't be loaded, which will reduce crashes.

this basically flips the order of operations around from previously

  1. loading resource packs
  2. parsing the metadata
    to
  3. reading metadata from the zip or filesystem
  4. loading resource packs only if the metadata indicates they can be loaded

optimally this would be the new process, but i had to take a few shortcuts to quickly get it ready for the dome patch

  • get all the mod paths
  • validate all required files exist in the zip/unpacked folder
  • load text data from the paths
  • init the mod data from the collected file paths
  • read manifest and add to mod data
  • read config and add to mod data
  • check the mod profile
  • filter and collect disable reasons
    • not loadable due to errors
    • mod enabled/disabled in profile
    • wrong game version -> if lower.. major: off, minor: log warn, patch: log info
    • wrong mod loader version -> same logic
  • figure out which files get hooks
  • dynamically generate pck only for those files
  • apply pck
  • load mods that don't have errors
  • apply extensions and so on

shortcut way

  • get all the mod paths
  • load text data from the paths
  • minimally init the mod data
  • read manifest and add to mod data
  • filter and collect errors/disable reasons (only version and bad manifest)
  • load mods that don't have errors
  • apply extensions and so on

Other notes for the future

  • mod data will always be available
    • so all mod state should be stored in it, it becomes the source of truth
  • deprecate Store ml_options.disabled_mods and locked_mods
  • i lost some logging when reading mod metadata
  • profile _set_mod_state can force to enable a mod even with load warnings
  • kinda disabled loading unpacked mods outside the editor
  • had to disable reload_mods

KANAjetzt and others added 30 commits August 22, 2024 08:58
The plan is to automate the creation of hooks for each function in all GDScripts.
Vanilla functions are renamed, and at the bottom of the script, mod loader hooks are generated. These hooks replace the vanilla functions and handle calls to the `callable_stack`.
Currently, I'm stuck on this approach because the method list's return property does not contain any information if there is no return type defined for the method.
That seems to work for filtering out methods that have a return.
Without removing the `class_name` before initializing the script on export, I get this parse error: `hides a global script class`. With these code changes, I get `Value of type [script_path] cannot be assigned to a variable of type [class_name]` if there is a self-assignment somewhere in the script.
Also added a check for static functions and currently working on recognizing inner classes.
check the `type_hint` and add it if it's present.
To check if the function has no space before `func` or `static`, used to ignore functions in inner classes. Later, we might want to figure out a way to include these.
Also removed extra comma in args list
until now all scripts received the "Mod Loader Hooks" header at the bottom
even when no changes where made.
used to ignore setter funcs
Also refactored some of the function/field names to improved the modding experience
added getter method, made both static and moved getter/setter check to is_moddable check
improved performance by exiting early when nothing is hooked
and a first style and type pass
@KANAjetzt KANAjetzt added the 4.x label Nov 8, 2024
@KANAjetzt KANAjetzt added this to the 4.x - 7.0.0 milestone Nov 8, 2024
Copy link
Member

@KANAjetzt KANAjetzt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Massive rework - thanks a lot! 🩵 This will require intensive testing, but I’m sure with this implemented in Dome Keeper, we will find all the issues 😄

I’ll start testing this with my projects. Currently, I don’t see anything holding us back from merging it 👍

"Loading any resource packs (.zip/.pck) with `load_resource_pack` will WIPE the entire virtual res:// directory. ",
"If you have any unpacked mods in ", _ModLoaderPath.get_unpacked_mods_dir_path(), ", they will not be loaded. ",
"Please unpack your mod ZIPs instead, and add them to ", _ModLoaderPath.get_unpacked_mods_dir_path()), LOG_NAME)
ModLoaderStore.has_shown_editor_zips_warning = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does only_once not work in this case?

static func warning(message: String, mod_name: String, only_once := false) -> void:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should, yeah. i didn't change anything here, just moved it.

# Application's Steam ID, used if workshop is enabled
steam_id = 0,
# Application's version following semver
semantic_version = "0.0.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if a game uses non semantic version numbers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then they can either leave it like this, which essentially disables version checking, implement their own logic since we can't preemtively cover all strange version standards, or switch to semver, which is probably the easiest. it's the main version standard for a reason

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could add an option to enable or disable this, or even allow the developer to implement their own validation. Just a thought for the future.

Copy link
Collaborator Author

@Qubus0 Qubus0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followup pr todo

  • move manifest file loading to file.gd
  • fix reload mods
  • doc comments for hooks
  • use log only_once instead of ModLoaderStore.has_shown_editor_zips_warning

"Loading any resource packs (.zip/.pck) with `load_resource_pack` will WIPE the entire virtual res:// directory. ",
"If you have any unpacked mods in ", _ModLoaderPath.get_unpacked_mods_dir_path(), ", they will not be loaded. ",
"Please unpack your mod ZIPs instead, and add them to ", _ModLoaderPath.get_unpacked_mods_dir_path()), LOG_NAME)
ModLoaderStore.has_shown_editor_zips_warning = true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should, yeah. i didn't change anything here, just moved it.

# Application's Steam ID, used if workshop is enabled
steam_id = 0,
# Application's version following semver
semantic_version = "0.0.0",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then they can either leave it like this, which essentially disables version checking, implement their own logic since we can't preemtively cover all strange version standards, or switch to semver, which is probably the easiest. it's the main version standard for a reason

Copy link
Contributor

@pirey0 pirey0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure about "macOS" working correctly over "macos"?

var game_install_directory := OS.get_executable_path().get_base_dir()

if OS.get_name() == "OSX":
if OS.get_name() == "macOS":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from what i can see in the c++ code the feature flag is "macos" (no capitalization), is it working correctly with the capitalized OS?
image
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that's get_name() though, not the flag. confused me too. we should probably decide on a single one - i think it was originally chosen because has_feature was the same with web and without, which has changed since

return {}
ModLoaderLog.error("Can't open workshop folder %s (Error: %s)" % [workshop_folder_path, error_string(DirAccess.get_open_error())], LOG_NAME)
return []
var workshop_dir_listdir_error := workshop_dir.list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: workshop_dir_listdir_error could just be result or error

Copy link
Collaborator Author

@Qubus0 Qubus0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no new todos i'll fix those right away

var game_install_directory := OS.get_executable_path().get_base_dir()

if OS.get_name() == "OSX":
if OS.get_name() == "macOS":
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that's get_name() though, not the flag. confused me too. we should probably decide on a single one - i think it was originally chosen because has_feature was the same with web and without, which has changed since

@Qubus0 Qubus0 added this pull request to the merge queue Nov 12, 2024
Merged via the queue into GodotModding:4.x with commit 2892c8b Nov 12, 2024
1 check passed
@Qubus0 Qubus0 deleted the feat_zip_peeking branch November 12, 2024 10:48
@KANAjetzt KANAjetzt mentioned this pull request Nov 13, 2024
31 tasks
@KANAjetzt KANAjetzt linked an issue Nov 29, 2024 that may be closed by this pull request
@KANAjetzt KANAjetzt added the validation Feature to make things safe and predictable label Mar 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.x enhancement New feature or request validation Feature to make things safe and predictable

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loading prevention for incompatible game version

3 participants