miniMAXIme: Character scaler and selector

by Pi-C

The player stands like a giant near the vehicles. Now you can adjust the size of your character -- and you can change your appearance at any time if you've other mods that provide extra characters!

Tweaks
11 days ago
0.16 - 2.0
26.4K

b [Fixed?] register_characters remote interface doesn't really work

1 year, 4 months ago

Hi, I'm the author of !skins (skins-factored), and I'm trying to use the 'register_characters' remote interface to add skins that my library creates. However, there's two separate bugs with how the interface adds the characters.
Note: this post is quite long and detailed because I want to include as much information as is helpful to try and fix the issue. I also have a 13 MB log file with minime debug output enabled if you want to see that (I kept track of the noteworthy stuff in it so you don't have to sift through all the debug output, there's a lot!)

Bug #1:
Adding a character in script.on_load fails to do anything because minime's on_configuration_changed resets the global.minime_characters table, removing the entry the remote interface just created.
Here's a "stack trace" of what happens:
- the global.minime_characters table is reset at scripts/character.lua:728 in minime_character.make_character_list()
- which is called in minime_character.update_character_list(): scripts/character.lua:860
- which is called in init(): control.lua:210
- which is called in script.on_configuration_changed: control.lua:330
- on_configuration_changed runs after skins-factored loads and calls the remote interface in it's script.on_load handler
Unfortunately, skins-factored cannot depend on minime to make minime load before it, as that would cause a circular dependency (skin mod -> !skins -> minime -> skin mod).

A fix for this that I can think of would be to keep track of which characters are added by the remote interface, and re-add them in make_character_list() after adding the characters found by pattern searching and from "CharSelect", at around line 830 in character.lua. This would require passing the old list to make_character_list() and having it check each skin for a flag like "from_remote_interface" or something like that.

Bug #2:
Adding a character during runtime doesn't seem to create the data in global.minime_character_properties, causing issues when minime expects that data to exist (such as when Jetpack raises the on_character_swapped event).
The remote interface calls minime_character.set_character_data() to add the character to global.minime_characters, but entries are only added to global.minime_character_properties by minime_character.make_character_list() in scripts/character.lua:836, which only runs during init().

I think a fix for this would be to also run global.minime_character_properties = get_minime_character_properties() when interface_functions.register_characters() is called.

For reference, the code I'm running to call the interface looks like this:

if script.active_mods["minime"] then
  local skin_prototypes = {}
  for _, skin in ipairs(Common.available_skins) do
    table.insert(skin_prototypes, Common.skin_to_prototype(skin))
  end

  log("minime compat: " .. serpent.line(skin_prototypes))
  remote.call("minime", "register_characters", skin_prototypes)
  log("called")
end

skin_prototypes is just a list of the prototypes of each character skin my mod has generated (example: {"character", "character-gear-girl"} (yes character shouldn't be in there but minime just ignores it))

If there's any other information that I can provide that would be helpful for figuring this out, I'd be glad to provide it! minime is a really impressive mod, and it's clear you've put a lot of effort into it :)

1 year, 4 months ago
(updated 1 year, 4 months ago)

Well, after spending the rest of the day debugging & testing stuff, it seems Bug #1 was probably caused by me misunderstanding the Factorio data lifecycle. I moved the remote interface call to an on_nth_tick handler that immediately runs and then unregisters itself, and now the skins do get added all the time (basically it's an on_init_final_fixes if you will ;)

However, Bug #2 still exists unchanged, even when calling the remote interface during runtime. Notably the bug does not occur for characters added through other means (namely the engineer), as the data does exist for them; it's only missing for remote interface characters.

1 year, 4 months ago

Hi! Thanks for your detailed bug report!

Adding a character in script.on_load fails to do anything because minime's on_configuration_changed resets the global.minime_characters table, removing the entry the remote interface just created.

Adding characters in script.on_load would never work because the global table is only readable at that point.

it seems Bug #1 was probably caused by me misunderstanding the Factorio data lifecycle. I moved the remote interface call to an on_nth_tick handler that immediately runs and then unregisters itself

Oh, yes, the joys of the Factorio data lifecycle -- I definitely know what you mean! Nearly went crazy myself when trying to figure out how to best take care of (re)initializing my tables and respond to calls to my remote functions. IIRC, I finally went with the assumption that mods wanting to register their characters would add a dependency on miniMAXIme, but I didn't reckon with mods that can't do this.

Regarding the second bug:

I think a fix for this would be to also run global.minime_character_properties = get_minime_character_properties() when interface_functions.register_characters() is called.

I'll have to look into that. I think I didn't do this because it would rebuild the complete list whenever a mod registered a new character. Allowing mods to pass on a bunch of characters at once already is an optimization over rebuilding the list after registering each individual character, but it doesn't help if several different mods register characters.

minime is a really impressive mod, and it's clear you've put a lot of effort into it :)

Thanks for the compliment! The mod has come a long way since I've naively decided to add the character selector part, without really knowing what I'd get myself into. :-D

1 year, 4 months ago

I've sent you a PM with my latest WIP version on the forum. Please try if that fixes the crash! :-)

1 year, 4 months ago

The WIP version didn't fix the bug, but I made a couple small edits and managed to get it working!

First, at the beginning of get_minime_character_properties() where you added the check for calling for a single player, there's a sneaky little bug here:
@ scripts/character.lua:240

chars = global.minime_characters and
        global.minime_characters[character] and
        { character = global.minime_characters[character] }

I'm pretty sure that last line should actually be { [character] = global.minime_characters[character] }. As it is, the literal string "character" is used as the key, which means the base_name of every character is set to "character". With it in brackets the value of character is used, which sets the base name of characters and their jetpack variant to be the normal version of the character, which I think was the intent.

Then, in interface_functions.register_characters() you call get_minime_character_properties(char), which generates the character properties for it as well as the jetpack & bobs variants, but then you only put the properties for the base character into the global.minime_character_properties table. Thus, the jetpack characters still don't have properties and the crash still happens.
Instead of global.minime_character_properties[char] = tmp_data and tmp_data[char], I wrote
@ scripts/remote_interface.lua:55

for c_name, c_data in pairs(tmp_data) do global.minime_character_properties[c_name] = c_data end
minime.show("global.minime_character_properties", global.minime_character_properties)

and that worked (debug printing not necessary obviously).

1 year, 4 months ago

Thanks for the hints! I've found some more things that needed fixing, just uploaded the new version. :-)

New response