Problems with game.is_multiplayer()

Place to get help with not working mods / modding interface.
Post Reply
Pi-C
Smart Inserter
Smart Inserter
Posts: 1645
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Problems with game.is_multiplayer()

Post by Pi-C »

Some time ago I got this crash reported on the mod portal:
https://mods.factorio.com/mod/Bio_Industries/discussion/60d0d35b4907807d4bfeb51e wrote: Error ServerMultiplayerManager.cpp:91: MultiplayerManager failed: "The mod Bio Industries (1.1.10) caused a non-recoverable error.
Please report this error to the mod author.
Error while running event Bio_Industries::on_configuration_changed
Bio_Industries/settings_changed.lua:34: attempt to index field '?' (a nil value)
stack traceback:
Bio_Industries/settings_changed.lua:34: in function 'musk_floor'
Today, somebody else sent me the same error message. I couldn't reproduce the crash, but something smells fishy. This is the code in question:

Code: Select all

29:  -- Always use dummy force if option is set
30:  if BioInd.UseMuskForce then
31:    force = BioInd.MuskForceName
32:  -- Singleplayer mode: use force of first player
33:  elseif not game.is_multiplayer() then
34:    force = game.players[1].force.name
35:  -- Multiplayer game
36:  else
Never mind the "attempt to index field '?' error -- that's easy to fix. But the line where the crash occurs is hidden behind "if not game.is_multiplayer()" -- as both crashes were reported for servers of a multiplayer game, this line shouldn't be reached!

Trying to reproduce the crash, I added code for debugging messages:

Code: Select all

  -- Always use dummy force if option is set
  if BioInd.UseMuskForce then
    force = BioInd.MuskForceName
  -- Singleplayer mode: use force of first player
  elseif not game.is_multiplayer() then
BioInd.show("game.is_multiplayer() -- should be single player", game.is_multiplayer())
    force = game.players[1].force.name
  -- Multiplayer game
  else
BioInd.show("game.is_multiplayer() -- should be multiplayer", game.is_multiplayer())
I then used "Multiplayer --> Host new game" from the main menu, saved the game, quit and changed a startup setting (just so that on_configuration_changed would run the code), restarted Factorio, and used "Multiplayer --> Host saved game" to load the game. As expected, I found this in the logfile:

Code: Select all

5623.554 Script @__Bio_Industries__/common.lua:212: Startup settings changed. Calling settings_changed.musk_floor()!
5623.554 Script @__Bio_Industries__/common.lua:212: game.is_multiplayer() -- should be multiplayer: true
I quit the game again and used "Single player --> Load game" to load the same game. Although the multiplayer game has been loaded in singleplayer mode, the logfile did not change:

Code: Select all

5761.514 Script @__Bio_Industries__/common.lua:212: Startup settings changed. Calling settings_changed.musk_floor()!
5761.514 Script @__Bio_Industries__/common.lua:212: game.is_multiplayer() -- should be multiplayer: true
Not quite as expected (especially as that game was a multiplayer game with just one player), but OK -- perhaps the save file contains some info that the game was started originally as a multiplayer game. However, once on_configuration_changed was finished, I tried this on the console:

Code: Select all

/c game.print(game.is_multiplayer())
Astonishingly, the result of the command is now "false".

Basically the same thing happens when I start a new game in singleplayer mode, save, and host it as a multiplayer game:

Code: Select all

-- Logfile:
  72.732 Script @__Bio_Industries__/common.lua:212: Startup settings changed. Calling settings_changed.musk_floor()!
  72.732 Script @__Bio_Industries__/common.lua:212: game.is_multiplayer() -- should be single player: false

-- Console:
/c game.print(game.is_multiplayer())
true
Is that normal behavior, or should I report the inconsistent results of game.is_multiplayer() as a bug?
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

User avatar
boskid
Factorio Staff
Factorio Staff
Posts: 2241
Joined: Thu Dec 14, 2017 6:56 pm
Contact:

Re: Problems with game.is_multiplayer()

Post by boskid »

This is unlikely to be considered a bug. For the game state determinism (when creating a replay), every change of a game state has to be introduced by an input action. The same is true with the `game.is_multiplayer()` result value - because you can read it in lua it is part of the game state. There is an InputAction which is created when starting a game in SP (SingleplayerInit) or MP (MultiplayerInit) which changes value of a boolean which is returned by the `game.is_multiplayer()`. Input actions are only processed when the control scripts are already loaded because code that handles them may raise lua events so not having control scripts loaded would result in a replay that desyncs (since in replay all scripts are loaded once for the entire replay duration and are not reloaded when the save file transitions between SP and MP).

From the other end, your code runs during on_configuration_changed callback. This is one of those events which will break the replay as there are changes which are not introduced by InputActions. This callback is called during savefile loading which happens way earlier than processing of input actions so the `game.is_multiplayer()` will report old value here as none of the SingleplayerInit or MultiplayerInit actions were processed yet.

This behavior is because the game state was not yet told that it is no longer SP when starting MP or no longer MP when starting SP.

Post Reply

Return to “Modding help”