Kagebunshin_mode

by fumoli

A mod that allows you to create clones that will run and scout the map.. Also, just for fun, you can add a trail of different parkicles behind the character model.

Tweaks
6 months ago
1.1
97
Environment Cheats

b Crash on loading game

9 months ago
(updated 9 months ago)

Loading an existing game that had been saved while your mod was active I got this crash:

The mod Kagebunshin_mode (2.2.2) caused a non-recoverable error.
Please report this error to the mod author.

Error while running event Kagebunshin_mode::on_player_joined_game (ID 45)
Gui element with name hello_button already present in the parent element.
stack traceback:
  [C]: in function 'add'
  __Kagebunshin_mode__/button.lua:7: in function 'createHelloButton'
  __Kagebunshin_mode__/button.lua:30: in function <__Kagebunshin_mode__/button.lua:27>

Usually, the event on_player_joined_game will only be entered once in singleplayer games (after the player has been created), but in multiplayer mode, it's run each time a player connects. So you should check that the player doesn't have a valid button before you add it:

local function createHelloButton(player)
    local button = player.gui.top["hello_button"]
    if not (button and button.valid) then
        button = player.gui.top.add{
            type = "button",
            name = "hello_button",
            caption = "Spawn Clone",
            tooltip = "Нажмите, чтобы заспавнить клонов"
        }
    end
    return button
end

Also, player.gui is shared between all mods, so using the name "hello_button" for a GUI element is not a good idea! Better prepend it with a string that other mods won't use: like the internal name of your mod, something like "Kagebunshin_hello_button".

Another thing: the tooltip is not meant to be a fixed string! Not everybody understands Russian, so make sure the tooltip can be read by everybody. You can do this by using a localised string. (Ideally, you'd provide at least an English translation in addition to your Russian string. But if you provide a locale file as framework that other mods can use, authors of translation mods can add translations to your mod.)

9 months ago

Loading an existing game that had been saved while your mod was active I got this crash

I fixed it in a local copy, today or tomorrow I will upload it along with other fixes.

Another thing: the tooltip is not meant to be a fixed string! Not everybody understands Russian, so make sure the tooltip can be read by everybody. You can do this by using a localised string. (Ideally, you'd provide at least an English translation in addition to your Russian string. But if you provide a locale file as framework that other mods can use, authors of translation mods can add translations to your mod.)

Yes, I am aware of this. This is one of the many things I didn't pay attention to. My studies have just begun, the load on me has increased, so I don’t remember everything that I made a note in my head about.

By the way, many thanks not only for describing these bugs and problems, but also for providing a solution.

9 months ago
(updated 9 months ago)

Hi, again! Thanks for fixing the bugs! :-)

My studies have just begun, the load on me has increased, so I don’t remember everything that I made a note in my head about.

I've had a look again and found some more issues. Let's start with button.lua:

  • In function createHelloButton:

    local function createHelloButton(player)
    local button = player.gui.top["kagebunshins_spawn"]

This works alright, but isn't looking really nice. Your way will place the button at the very top of the screen. The recommended way is to put it into a frame that's at the top of the screen ('button_flow'):

mod_gui = require('mod-gui')

local function createHelloButton(player)
    local button_flow = mod_gui.get_button_flow(player)
    local button = button_flow["kagebunshins_spawn"]

For defining the button, try this:

    button = player.gui.top.add{
        type = "button",
        name = "kagebunshins_spawn",
        caption = {"kagebunshin-gui.caption"},
        tooltip = {"kagebunshin-gui.tooltip"}
    }

That will work if you create a file locale/en/gui.lua:

[kagebunshin-gui]
caption=Clones
tooltip=Click to spawn clone

(Don't put spaces around the '='! caption = Clones would mean that the field name is 'caption ' instead of 'caption', and the translation would become ' Click to spawn clone', with a leading space.)
You can add translations to other languages by creating a new sub directory in 'locale', copying all files in locale/en to the new folder, and replace the texts on the right side of the '='. If no locale is found for a language, English ('en') will be used as fallback.

  • For function onHelloButtonClick, I have an idea: Sending the clone in the direction of the closest chunk isn't really intuitive because you usually don't see the grid with the chunk borders. How about using the direction in which the player's character is moving (player.character.direction), and falling back to the direction of the closest chunk if the player doesn't have a valid character?

  • You create a button for each player who has joined the game, but if any of these buttons is clicked, you always spawn a clone of game.players[1]. The on_gui_click event passes on player_index, so it's trivial to call onHelloButtonClick() with the correct player:

    if event.element and event.element.name == "kagebunshins_spawn" then
    onHelloButtonClick(game.players[event.player_index])
    end

File clones.lua has some big problems!

  • The character data you save in kagebunshins_coords won't persist between saves because they aren't stored in the global table. That's just a slight nuisance in single player mode, but will cause desyncs in multiplayer games. Remember: Factorio distinguishes between "a global table" (any table that has not been declared with the keyword local), and "the table global", which will be stored in saved game. Make sure that you rename all instances of kagebunshins_coords to global.kagebunshins_coords and you're on the safe side.

  • Entities should never be used as table index! According to Bilka (one of the devs), this will also cause desyncs. Instead, use a structure like this:

    kagebunshins[character.unit_number] = character

    kagebunshins_cords[character.unit_number] = {
    old_x = character.position.x,
    old_y = character.position.y
    }

  • In control.lua, there's something fishy in your handler for the on_tick event:

    script.on_event(defines.events.on_tick,
    function(event)
    --Узнаём путь до близжайсшего чанка(внести в отдельное событие.)

      if event.tick % 1 == 0  then
     …
      end
    end
    

    end)

The check for event.tick %1 == 0 is just a waste of computing power because the condition is always true. Something like this only makes sense if you want to wait for >1 ticks between actions.

9 months ago
(updated 9 months ago)

I've had a look again and found some more issues. Let's start with button.lua:

  • In function createHelloButton:

    local function createHelloButton(player)
    local button = player.gui.top["kagebunshins_spawn"]

This works alright, but isn't looking really nice. Your way will place the button at the very top of the screen. The recommended way is to put it into a frame that's at the top of the screen ('button_flow'):

mod_gui = require('mod-gui')

local function createHelloButton(player)
  local button_flow = mod_gui.get_button_flow(player)
    local button = button_flow["kagebunshins_spawn"]

For defining the button, try this:

    button = player.gui.top.add{
        type = "button",
        name = "kagebunshins_spawn",
        caption = {"kagebunshin-gui.caption"},
        tooltip = {"kagebunshin-gui.tooltip"}
    }

That will work if you create a file locale/en/gui.lua:

[kagebunshin-gui]
caption=Clones
tooltip=Click to spawn clone

(Don't put spaces around the '='! caption = Clones would mean that the field name is 'caption ' instead of 'caption', and the translation would become ' Click to spawn clone', with a leading space.)
You can add translations to other languages by creating a new sub directory in 'locale', copying all files in locale/en to the new folder, and replace the texts on the right side of the '='. If no locale is found for a language, English ('en') will be used as fallback.

https://ibb.co/nktMj4b

Is it normal that the frame appears to the left of the button?

locale/en/gui.lua:

If I'm not mistaken, that file should be .cfg, but in any case the problem does not go away.

The check for event.tick %1 == 0 is just a waste of computing power because the condition is always true. Something like this only makes sense if you want to wait for >1 ticks between actions.

Previously, this was used as a crutch to artificially slow down the spawning of clones. Since some functions were executed directly in events.on_tick, they heavily loaded the system. Then, when relative order was brought into the code to the best of our ability, in one of the local versions that line was removed, but due to the fact that I use git extremely crookedly, it somehow ended up in production.

The remaining edits require more thoughtful consideration. So as soon as I get my hands on the mod, I'll come back to this post again.

Thank you again for your help in creating the mod.

9 months ago

https://ibb.co/nktMj4b

Is it normal that the frame appears to the left of the button?

No, made a mistake there: With the first change, we get the button_flow instead of player.gui.top, but if that doesn't exist, we still create the button at the top level, so both the frame of the button flow and the button are children of player.gui.top. This should really work:

-- Создаем новую кнопку
local function createHelloButton(player)
  local button_flow = mod_gui.get_button_flow(player)
  local button = button_flow["kagebunshins_spawn"]
  if not (button and button.valid) then
    button = button_flow.add{
      type = "button",
      name = "kagebunshins_spawn",
      caption = "Clones",
      tooltip = "Click to spawn clone"
    }
  end
  return button
end

locale/en/gui.lua:

If I'm not mistaken, that file should be .cfg, but in any case the problem does not go away.

Yes, correct. Made another mistake! :-D

The check for event.tick %1 == 0 is just a waste of computing power because the condition is always true. Something like this only makes sense if you want to wait for >1 ticks between actions.

Previously, this was used as a crutch to artificially slow down the spawning of clones. Since some functions were executed directly in events.on_tick, they heavily loaded the system. Then, when relative order was brought into the code to the best of our ability, in one of the local versions that line was removed, but due to the fact that I use git extremely crookedly, it somehow ended up in production.

Yes, I thought it was likely some remnant from experimenting with different delays. Just comment it until you want to change the delay again. :-)

The remaining edits require more thoughtful consideration. So as soon as I get my hands on the mod, I'll come back to this post again.

OK, take your time …

Thank you again for your help in creating the mod.

You're welcome!

New response