Space trains


Electric trains designed to operate in space too. For Space Exploration mod.

Content
2 years ago
1.1
3.78K
Trains

i Compatibility with Multiple Unit Train Control

2 years ago

Hello! I really like this mod and want to use it on my next SE playthrough. I am the developer of the mod Multiple Unit Train Control, which makes locomotives twice as powerful when they are back-to-back (like the backwards one is helping too). Your mod is almost compatible with MUTC already, which is impressive for an electric train mod!

The only thing my mod breaks is the solar train generation. For that to work, somehow the "powerList" list needs to include 'space-trains-locomotive-solar1-mu' and so on. Would you like to add this feature, either by coding on your end or by a remote interface I can use? Or did you already optimize the electric trains for bidirectional operation and don't need MUTC to double their power? I can add them to the MUTC blacklist so it doesn't interfere.

2 years ago

Hello, i am happy to see someone is using this mod.
As it is my first mod, powerList was used as simple way to get needed info. I will try to look into it - maybe i can add that info to prototypes, or by looking onto prefixes of names...
I am not certain it will be done today though...

I didn't directly optimazed it for bidirectional, but reversing_power_modifier = 1.0, and i think they are still somewhat OP...

2 years ago

The reverse_power_modifier only affects manual driving in trains without any backwards-facing locomotives. In automatic, backwards locos always Pierce provide zero power. But I agree there probably isn't a need for my mod based on the speed and power you have there.

2 years ago

I now checks all train prototypes for prefixes of their names - if prefix of name is already powered train, it will be added to list with those settings.
I also added interface "space-trains":

SetPowerProduction (name, isSolar, power)
- will add/set prototype with "name"
- isSolar determines, if it is powered by light or constant production (like rtg)
- power is in KW

AddTrainEnergy (train, energy)
- will add (or substract) "energy" in Joules to entire train and redistributing it
- returns actual amount of energy used (if batteries = fuel is too full)

2 years ago

Hey, so your solution works! Basically, at least. I'm honestly a little surprised it works as-is because it doesn't follow the conventions for how to use global variables and initialization events. It will help to read https://lua-api.factorio.com/latest/Data-Lifecycle.html and https://lua-api.factorio.com/latest/LuaBootstrap.html for how to use the global variable table and init functions.

Usually when you have parameters that need to be configured on startup, you store them in the global table so they are persisted across game save/load. Then you update them in both on_init (which triggers only when the save is created or this mod is installed) and on_configuration_changed (which triggers when this mod is upgraded and when other mods are installed, upgraded, or uninstalled). That way if someone installed my mod after playing the game with your mod, your mod would get the on_configuration_changed event and find the new prototypes my mod added.

It's even more important to do this for lists of entities in the savegame. They should go in global, then you don't need to rebuild the list every time you load a map. chargerList, chargerLocomotives, and spaceTList fall into this type. on_init creates the empty tables. Normal events just add and subtract items from the list as needed. You can put the "recheck everything" scans in on_configuration_changed, in case another mod being uninstalled made something disappear.

There's a little more to it than that. I'm happy to answer more questions, let's get this mod running reliably!

2 years ago

Hey, so your solution works! Basically, at least. I'm a little surprised it works as-is because it doesn't follow the conventions for how to use global variables and initialization events. It will help to read https://lua-api.factorio.com/latest/Data-Lifecycle.html and https://lua-api.factorio.com/latest/LuaBootstrap.html for how to use the global variable table and init functions.

Usually when you have parameters that need to be configured on startup, you store them in the global table so they are persisted across game save/load. Then you update them in both on_init (which triggers only when the save is created or this mod is installed) and on_configuration_changed (which triggers when this mod is upgraded and when other mods are installed, upgraded, or uninstalled). That way if someone installed my mod after playing the game with your mod, your mod would get the on_configuration_changed event and find the new prototypes my mod added.

It's even more important to do this for lists of entities in the savegame. They should go in global, then you don't need to rebuild the list every time you load a map. chargerList, chargerLocomotives, and spaceTList fall into this type. on_init creates the empty tables. Normal events just add and subtract items from the list as needed. You can put the "recheck everything" scans in on_configuration_changed, in case another mod being uninstalled made something disappear.

There's a little more to it than that. I'm happy to answer more questions, let's get this mod running reliably!

2 years ago

Hey, so your solution works! I'm a little surprised it works as-is because it doesn't follow the conventions for how to use global variables and initialization events. Local variables you create in your Lua instance (the control.lua file) get deleted every time you close the game or map, which can cause desyncs in multiplayer when people re-load the map at different times, and weird things to happen if they don't get regenerated exactly the same way every time you load a map.

It will help to read https://lua-api.factorio.com/latest/Data-Lifecycle.html and https://lua-api.factorio.com/latest/LuaBootstrap.html for how to use the global variable table and init functions.

Usually when you have parameters that need to be configured on startup, you store them in the global table so they are persisted across game save/load. Then you update them in both on_init (which triggers only when the save is created or this mod is installed) and on_configuration_changed (which triggers when this mod is upgraded and when other mods are installed, upgraded, or uninstalled). That way if someone installed my mod after playing the game with your mod, your mod would get the on_configuration_changed event and find the new prototypes my mod added.

It's even more important to do this for lists of entities in the savegame. They should go in global, then you don't need to rebuild the list every time you load a map. chargerList, chargerLocomotives, and spaceTList fall into this type. on_init creates the empty tables. Normal events just add and subtract items from the list as needed. You can put the "recheck everything" scans in on_configuration_changed, in case another mod being uninstalled made something disappear.

There's a little more to it than that. I'm happy to answer more questions, let's get this mod running reliably!

2 years ago

Well, it should work - local variables contains only cached constants based on installed mods and startup settings, and those should be the same on all clients resulting in same data. Only dynamic data are stored in global, i didn't wanted to pollute global with them, especially when they are created fast.
I readed about data cycle, but i wanted to debug control.lua and you don't needs to restart entire game for those changes, only load save-game. Saddly, on_init is not fired on load (and on_configuration_changed too), so it was safer to call it everytime.
I could move those constants to global and refill them only on events, but i think they are polluted fast and not slowing down the game.
Maybe i can change it in the future and even cache list of trains and change it on events only, but currently i don't have much time for that.
But thanks for advices.

2 years ago
(updated 2 years ago)

Yeah, when I'm testing changes to my global variable setups I do "fake" version number changes to force on_configuration_changed to work. It's part of the process. The global table is explicitly intended to cache parts of the game state you need frequently, in order to avoid expensive calls to the game engine like LuaSurface::find_filtered_entities(). The only reason it's in the save file is so that your code doesn't need to know when the game was saved and loaded. You can purge the global table periodically, but it's a lot faster than regenerating it every time.

Your local variables will work for static values like you said. It will break if anyone decides to use the remote interface:

  1. Server loads map with your mod, and a new mod added for the first time.
  2. Your mod initializes the local variable table based on prototypes and settings.
  3. New mod gets to on_init and calls your remote interface, because it has to wait until it knows your Lua instance is running (and to follow the convention).
  4. Remote interface adds new mod's entities to the local variable table in the server's Lua instance.
  5. Player joins the server. Server sends save file to player, which does not include local variables.
  6. Player's game starts and initializes the local variable based on prototypes and settings.
  7. Player's game never receives the call to your remote interface, because on_init already ran for this save.
  8. Desync kicks the player from the game, either because the local variables don't match, or the first time the server charges a NewMod locomotive and the player doesn't.

The same thing will happen in a single player game if you save and load it again. on_init in the other mod won't fire again, so the NewMod locomotives will only be registered the first time you load the save after adding the NewMod.

2 years ago

Yeah, when I'm testing changes to my global variable setups I do "fake" version number changes to force on_configuration_changed to work. It's part of the process. The global table is explicitly intended to cache parts of the game state you need frequently, in order to avoid expensive calls to the game engine like LuaSurface::find_filtered_entities(). The only reason it's in the save file is so that your code doesn't need to know when the game was saved and loaded. You can purge the global table periodically, but it's a lot faster than regenerating it every time.

Your local variables will work for static values like you said. It will break if anyone decides to use the remote interface:

  1. Server loads map with your mod, and a new mod added for the first time.
  2. Your mod initializes the local variable table based on prototypes and settings.
  3. New mod gets to on_init and calls your remote interface, because it has to wait until it knows your Lua instance is running (and to follow the convention).
  4. Remote interface adds new mod's entities to the local variable table in the server's Lua instance.
  5. Player joins the server. Server sends save file to player, which does not include local variables.
  6. Player's game starts and initializes the local variable based on prototypes and settings.
  7. Player's game never receives the call to your remote interface, because on_init already ran for this save.
  8. Desync kicks the player from the game, either because the local variables don't match, or the first time the server charges a NewMod locomotive and the player doesn't.

The same thing will happen in a single player game if you save and load it again. on_init in the other mod won't fire again, so the NewMod locomotives will only be registered the first time you load the save after adding the NewMod. Saving and loading the game will result in NewMod locomotives not charging anymore.

2 years ago

Thanks, i forgot about that interface. I moved those local helper tables to global and they are filled only on on_init and on_configuration_changed. I hope if someone calls SetPowerProduction, it calls it on on_configuration_changed too - because i recreate it anew.

I was not using faking mod version changes, because it needed complete game restart and if i did changes to control.lua only, i needed only to load saved game. Then i left calling on_init after each load there, because it did no harm to ma single-player run and i had no oportunity to test multiplayer.

2 years ago

Updates seem to work well, thank you!

I have some ideas to improve performance once players get to many planets with many many trains. If you ever have time and would like advice or help, let me know. It's certainly not a problem for me yet, but I've found users can do some remarkable things with our creations. ^_^

2 years ago

Thanks!
I know some things could be done better, but i don't think UPS cost is that high. I fear that some changes could also add some bugs.
Currently energy calculations are done each 15 frames, creating spikes. I assume it could be better it split them into smaller amounts and do calculations more often - but it could create some imbalance between each split and also increase complexity.
But most UPS cost should be from chargers (if someone is using them), because they are looking for trains in front of them. I don't know UPS cost of charging from internal baterries and i added them at the end of my gameplay.
So if you have some ideas (and they should not be too complicated), i could try to implement them.

2 years ago
(updated 2 years ago)

I too assume that people will mostly use solar trains when they are able.

Simple thing, the on_nth_tick event subscriber is available so you can avoid putting "tick % interval == 0" into on_tick. But you can only register one function per value of N, so make sure the train interval and charger interval are different unless they are in the same function.

You are right, the first problem will be choppiness from spikes of activity. You can probably get away with longer times between updates, and add more power each time to compensate. There are also tricks to decide how many updates to do each tick based on how many things there are.

Second biggest lag is running find_entities_filtered() all the time. This is why most mods keep a cached list of all their entities to update, and add/subtract from the list in the built/destroyed events.

Caching the list of trains that have your locomotives in them also makes sense. There will probably be lots of trains with none, and you don't have to search every surface every time.

The chargers could be optimized by only checking in front of them when an electric train just came to a stop nearby, and after some longer delay. Not sure exactly how this would work.

I agree these are not particularly simple, and can be put off.

New response