Another forces mod

This is the place to request new mods or give ideas about what could be done.
Post Reply
pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Another forces mod

Post by pinecoffin »

Hey all,

I don't know how to code except to sort of figure out the flow and have some success there and have been able to make a mod called "Pines Easier Start (Krastorio2)".

What I'm really really wanting to do is make or get access to a mod that:
  • All players start in the same location, at the crash site.
  • When a new player joins the server the mod creates a new force called the players name and moves the player to this force.
  • The mod makes the new forces 'friends' including with the 'player' force.
  • The mod enables 'cease fire' with the new forces including the 'player' force.
  • Share_chart is enabled for all forces
  • Would be very handy to have and offline force entities be protected.
I can do this via the editor but then it disables the modded acheivements list.

I have been trying to learn lua but have learning issue due to sleep issues (that are not caused becuase the factory must grow).
Any help with this would be amazing as have been trying to figure it out for ages and ages by looking at other mods and see if I could figure it.

Cheers in advance,
Pine

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Wed Oct 26, 2022 9:55 am
  • All players start in the same location, at the crash site.
  • When a new player joins the server the mod creates a new force called the players name and moves the player to this force.
  • The mod makes the new forces 'friends' including with the 'player' force.
  • The mod enables 'cease fire' with the new forces including the 'player' force.
  • Share_chart is enabled for all forces
Zip the attached file together with an info.json and you'll have a mod that does these things.

  • Would be very handy to have and offline force entities be protected.
Could be done with setting entity.destructible, entity.minable, entity.operable, and possibly even entity.active to false. However, there are two problems:
  • You must keep track of ALL entities built by each force. You'd register each entity in the handlers for the built events by storing the entity with the force, e.g. like this:

    Code: Select all

    local function register_entity(event_data)
      local entity = event.created_entity or event.entity
      local force = (entity.force and entity.force.name) or
                    (entity.player and entity.player.force.name) or
                    (entity.associated_player and entity.associated_player.force.name)
    
      if not force then
        return
      end
    
      global.force_data = global.force_data or {}
      global.force_data[force] = global.force_data[force] or {}
    
    
      -- Make this a table so we can add more data as needed
      global.force_data[force][entity.unit_number] = {
        entity = entity
      }
    end
    
    local built_events = {
      defines.events.on_built_entity,
      defines.events.on_robot_built_entity,
      defines.events.on_script_raised_built
    }
    script.on_event(built_events, register_entity)
    
    Things stored in the global table will be saved with the game, so that would bloat the size of your save files. Even if you remove all entries of invalid entities (listen to the mined, died, and destroyed events!), the size of each force's list will be considerable.
  • Say you don't care about save file size. You'd probably think that protecting your entities would mean to simply turn off everything when a player disconnects, and to turn it on again next time the player joins the game. However, there may be other mods that have changed one or all properties already, and your mod could break them when you activate the entity again. To avoid this, you'd also have to store the original state of all properties when you turn off the entity:

    Code: Select all

    script.on_event(defines.events.on_player_left_game, function(event)
      local player = game.players[event.player_index]
      local force = player.force
    
      local entity
      for e, entity_data in pairs(global.force_data[force.name]) do
        entity = entity_data.entity
        if entity and entity.valid then
          entity_data.active = entity.active
          entity.active = not entity.active
    
          entity_data.destructible = entity.destructible
          entity.destructible = not entity.destructible
    
          entity_data.minable = entity.minable
          entity.minable = not entity.minable
    
          entity_data.operable = entity.operable
          entity.operable = not entity.operable
        end
      end
    end)
    
    This would have to be reverted in on_player_joined_game (add to the function above):

    Code: Select all

      local entity
      for e, entity_data in pairs(global.force_data[player.force]) does
        entity = entity_data.entit
        if entity and entity.valid then
          entity.active = entity_data.active
          entity_data.active = nil
    
          …
        else
          global.force_data[player.force][e] = nil
        end
      end
    
    For big bases, that may take a while.
Attachments
control.lua
(2.44 KiB) Downloaded 39 times
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Re: Another forces mod

Post by pinecoffin »

Pi-C,

First thank you very much for doing this and spending the time to explain the issues that could be encountered by having base protection.
I actually put a mod on last night, "Protect Offline Forces", which works great, but my auto save files where at minimum double the size, and in a couple of instances five times the size. So we'll do without that feature.

Sadly when I run your mod I get the below error (I couldn't think what to call it so Coop Play it is for now.:

[
The mod Coop Play (0.1.0) caused a non-recoverable error.
Please report this error to the mod author.

Error while running event coop-play::on_player_joined_game (ID 45)
LuaForce doesn't contain key set_ceasefire.
stack traceback:
[C]: in function '__index'
__coop-play__/control.lua:29: in function 'policy'
__coop-play__/control.lua:57: in function <__coop-play__/control.lua:53>
]

Also an edit, looking at the script and please consider my little understanding, we don't want the 'player' force removed as mods like Krastorio have the crash site set to that which has usable assemblers etc.

Thanks very much again for your help.
Pine
Attachments
coop-play_0.1.0.zip
(1.46 KiB) Downloaded 39 times

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Thu Oct 27, 2022 7:04 am
Sadly when I run your mod I get the below error (I couldn't think what to call it so Coop Play it is for now.:

[
The mod Coop Play (0.1.0) caused a non-recoverable error.
Please report this error to the mod author.

Error while running event coop-play::on_player_joined_game (ID 45)
LuaForce doesn't contain key set_ceasefire.
stack traceback:
[C]: in function '__index'
__coop-play__/control.lua:29: in function 'policy'
__coop-play__/control.lua:57: in function <__coop-play__/control.lua:53>
]
Sorry for the typo: that should be set_cease_fire.
Also an edit, looking at the script and please consider my little understanding, we don't want the 'player' force removed as mods like Krastorio have the crash site set to that which has usable assemblers etc.
We don't remove that force -- we couldn't do it even if we wanted to, according to the description of game.merge_forces:
merge_forces (source, destination)
Marks two forces to be merged together. All entities in the source force will be reassigned to the target force. The source force will then be destroyed.



Notes
The three built-in forces -- player, enemy and neutral -- can't be destroyed. I.e. they can't be used as the source argument to this function.
All players will belong to force "player" when they are created (so they start at the same location). When on_player_joined_game triggers, they will be moved to their own custom force. Also, force "player" will inherit all entities left behind when a player is removed. As force "player" is friends with all other player forces, these entities can be used by all other players.
Thanks very much again for your help.
You're welcome! :-)
Last edited by Pi-C on Thu Oct 27, 2022 10:20 am, edited 1 time in total.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Re: Another forces mod

Post by pinecoffin »

Hi again Pi-C,

You are amazing. Thank you so much for setting this up, I've wanted this exact setup for years!

If you're happy for me to I'll upload it to the mod portal giving you full credit, unless you want to upload it yourself.

Wish I could think of a better name for it lol.

Thanks very much again,
pine

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Thu Oct 27, 2022 10:05 am
Hi again Pi-C,

You are amazing. Thank you so much for setting this up, I've wanted this exact setup for years!
Glad you like it!
If you're happy for me to I'll upload it to the mod portal giving you full credit, unless you want to upload it yourself.
Sure, go ahead and play around with it! I already have too many mods to take care of. I've just thought of one potential improvement: When the mod is added to a new game and players are moved to their own forces, they would all start at 0 researches. You could loop over game.forces["player].technologies and copy technology.researched to the new forces. I would do that only in response to on_init, which will call the same function as on_configuration_changed. To find out what the function was called from, I'd modify the init() function to look like this:

Code: Select all

local function init(event)

  local f_name
  for p, player in pairs(game.players) do
    f_name = player.force.name
    if player.name ~= f_name and
        not f_name:match("^"..script.mod_name.."%-"..player.name.."%-%d*$") then

      create_force(player)
      policy(player.force)

      -- Called from on_init
      if not event then
      	…
      end
          
    end
  end
end
This works because on_init will pass on no arguments, while you'll get event data for on_configuration_changed.
Wish I could think of a better name for it lol.
"Separate but equal" would be fitting if it wasn't so negatively connotated. Luckily, it's not my mod so I won't have to come up with a better name myself. :mrgreen:
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Re: Another forces mod

Post by pinecoffin »

Hi Pi-C,

I thought about adding the extra stuff with the research but most of the people I play with regularly would prefer to do their own from scratch. We're all wierd. ;-)

Ended up calling the mod Better Coop Play.

So there's another issue we've just discovered. We spent about five hours on the server yesterday and then jumped on today and we've lost our research and all the turrets are trying to kill us. Well they're not trying they're determined. And previously exposed map is gone.

So basically we've joined as new players even though we're not.

I'm just wondering if it would be better to create forces that use our login name and then making sure we re-join those forces rather than deleting them? Again keep in mind I barely now what I'm talking about here.

The mod was working awesomely up until this point. And if the fix requires we have to start a new map that's not a problem.

Also is as a side note that just occurred to me is there away to have new players get a copy of all of the map we've exposed so far?

Have really appreciated your help with this!

Regards,
Pine

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Sat Oct 29, 2022 7:11 am
Hi Pi-C,

I thought about adding the extra stuff with the research but most of the people I play with regularly would prefer to do their own from scratch. We're all wierd. ;-)
If you make your mod more general, it will appeal to more players. You could make that an option, based on a startup setting with the default value set to whatever you prefer.
Ended up calling the mod Better Coop Play.
Sounds good. :-)
So there's another issue we've just discovered. We spent about five hours on the server yesterday and then jumped on today and we've lost our research and all the turrets are trying to kill us. Well they're not trying they're determined. And previously exposed map is gone.

So basically we've joined as new players even though we're not.
Not new players, but on new forces. I've messed up the logic a bit:

Code: Select all

  -- Protect against players having the same name as an existing force
  local name = game.forces[player.name] and "" or player.name

  while name == "" or game.forces[name] do
    name = script.mod_name.."-"..player.name.."-"..math.floor(math.random(100))
  end
When this is run the first time, it will find players that have the same name as one of the default forces, and add a new force with a name MODNAME-PLAYERNAME-X, where X is a random number. (I added the random number because I'm not sure if player.name is unique.) For all other players, a new force PLAYERNAME will be created. On the second run, that force would already exist, so a new force would be created and the player would be moved from the old to the new force. Now, function policy() only sets cease_fire and friend of the player's force for "player" and forces with players. As there was only one player on the old force and the player has been moved to another force, there is no player left on the old force and it will be an enemy of the new force.
I'm just wondering if it would be better to create forces that use our login name and then making sure we re-join those forces rather than deleting them? Again keep in mind I barely now what I'm talking about here.
Stupid mistake! create_force() is called from init() which is run by on_init and on_configuration_changed. There is a check in init() so that create_force will only be run if player name and force name differ AND if the force name does not follow the pattern MODNAME-PLAYERNAME-X. However, create_force is also called by on_player_joined_game, without this check. So adding it should fix the bug:

Code: Select all

script.on_event(defines.events.on_player_joined_game, function(event)
  local player = game.players[event.player_index]
  local f_name = player.force.name

  if player.name ~= f_name and
      not f_name:match("^"..script.mod_name.."%-"..player.name.."%-%d*$") then

    create_force(player)
    policy(player.force)
  end
end)
Also is as a side note that just occurred to me is there away to have new players get a copy of all of the map we've exposed so far?
Isn't that what force.share_chart does?
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Re: Another forces mod

Post by pinecoffin »

Hi Pi-C,

I've just added it to the control.lua but the issue's still there. I checked via the editor and it's making a new force each time I join with a different number on the end and keeping the old numbers as well. It initially made "pinecoffin" but after that they're 'MODNAME-PLAYERNAME-X' as you mentioned. All still enemies as well.

What would be the reason to make it be 'MODNAME-PLAYERNAME-X' and not the players name ie in this case pinecoffin (I'm presuming Factorio.com doesn't let you create a name that's already in use though).

Edit: I've just done a test and even with verification off and playing only via LAN it won't let two players connect to a server with the same name.

I mentioned new players not getting a copy of the map as none of the new forces are getting it.

Sorry to be a pain in your back side. I really appreciate your work on this!

Have attached the control.lua and a screenshot of the forces.
Attachments
Screenshot  of forces in editor.PNG
Screenshot of forces in editor.PNG (15.81 KiB) Viewed 1644 times
control.lua
(2.76 KiB) Downloaded 37 times

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Sat Oct 29, 2022 6:11 pm
Hi Pi-C,

I've just added it to the control.lua but the issue's still there. I checked via the editor and it's making a new force each time I join with a different number on the end and keeping the old numbers as well. It initially made "pinecoffin" but after that they're 'MODNAME-PLAYERNAME-X' as you mentioned. All still enemies as well.
You've this block at lines 21-31:

Code: Select all

script.on_event(defines.events.on_player_joined_game, function(event)
  local player = game.players[event.player_index]
  local f_name = player.force.name

  if player.name ~= f_name and
      not f_name:match("^"..script.mod_name.."%-"..player.name.."%-%d*$") then

    create_force(player)
    policy(player.force)
  end
end)
But there still is this old block at lines 65-71:

Code: Select all

script.on_event(defines.events.on_player_joined_game, function(event)

  local player = game.players[event.player_index]



  create_force(player)
  policy(player.force)

end)
The old block is read after the new one and will overwrite the new function. Replace the block at 65-71 with the other one and you should be fine.
What would be the reason to make it be 'MODNAME-PLAYERNAME-X' and not the players name ie in this case pinecoffin (I'm presuming Factorio.com doesn't let you create a name that's already in use though).

Edit: I've just done a test and even with verification off and playing only via LAN it won't let two players connect to a server with the same name.
I've suspected this, but wasn't quite sure (testing multiplayer is always a PITA on my computer, running the game from two different accounts is really sloooow), so I appended a random number just in case two players would use the same name.
I mentioned new players not getting a copy of the map as none of the new forces are getting it.
That doesn't seem to be as easy as expected. I'll have to think about that some more …
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

pinecoffin
Burner Inserter
Burner Inserter
Posts: 11
Joined: Sat Jul 02, 2016 7:37 am
Contact:

Re: Another forces mod

Post by pinecoffin »

Hey Pi-C,

It's working!!!! Thank you, thank you, thank you.

I have been trying to figure this out all day and see if I could learn to do it myself as it was bugging the hell out of me not being able to do it. Or understand enough that the blocks of code were in the wrong place.

For adding the research option you mentioned, where would I place the code block in the script to make it work, and is there an easy way to have it prompt the new player if they want to accept the research or not. Also which force would it pick the research from ie how would it know who has the most researched?
Sounds complicated to me. :D Also not on my request so don't spend time on it. ;)

For the share_chart with new forces, it would certainly be a nice feature but I obviously don't know how to figure it out. Hopefully you feel like finding out and let me know :) I'm not sure if the PvP scenario does it. I'll see if I can test on my previous server which was that scenario.

Thank you so very much again.
Pine

Pi-C
Smart Inserter
Smart Inserter
Posts: 1648
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Another forces mod

Post by Pi-C »

pinecoffin wrote:
Sun Oct 30, 2022 11:52 am
Hey Pi-C,

It's working!!!! Thank you, thank you, thank you.
You're welcome!
I have been trying to figure this out all day and see if I could learn to do it myself as it was bugging the hell out of me not being able to do it. Or understand enough that the blocks of code were in the wrong place.
Basically, if there are two assignments to/definitions of variables/functions with the same name, the last one will overwrite the first. If you want to understand better what the code does, this is a good guide with many examples.
For adding the research option you mentioned, where would I place the code block in the script to make it work,
In the init() function.
and is there an easy way to have it prompt the new player if they want to accept the research or not.
I'm afraid not. The only way I can think of is to define a basic GUI, consisting of a table with the technology names + a checkbox for each tech, and a confirm button. The table would be built on the fly from all techs where tech.researched == true. When the confirm button is pressed, you'd set tech.researched to the state of its checkbox.
Also which force would it pick the research from ie how would it know who has the most researched?
My idea was that all players would start from the same state, so all techs that game.forces["player"] has researched would be copied to the new force.

About who has most researched: Run over force.technologies and count the techs where tech.researched == true:

Code: Select all

local force_techs = {}
for f, force in pairs(game.forces) do
 force_techs[f] = 0
  for t, tech in pairs(force.technologies) do
    if tech.researched then
      force_techs[f] = force_techs[f] + 1
    end
   end
end

local max_force = ""
local max = 0

for force, techs in pairs(force_techs) do
  if techs > max then
    max_force = force
    max = techs
  end
end
  
game.print(string.format("Force with most researched technologies: %s (%s)", max_force, max))
Of course, it would be better if you didn't have to run this all the time. You could use another table in global where you keep track of the researches of each force. Listen to on_research_finished/on_research_reversed and modify the entry for the force that has finished/reverted a research.
For the share_chart with new forces, it would certainly be a nice feature but I obviously don't know how to figure it out.
I'm really not sure what share_chart does. I've started a new game, walked eastwards for several chunks, then used

Code: Select all

/c game.create_force("test") game.player.force = "test"
and set both forces to be friends. But even after setting share_chart = true, the map only showed the vicinity of my character, not the way it walked while I was on the old force.

Perhaps something like this would work:

Code: Select all

script.on_event(defines.events.on_chunk_charted, function(event)

  for f, force in pairs(game.forces) do
    if force ~= event.force and next(force.players) and 
                                             not force.is_chunk_charted(event.surface_index, event.position) then
       force.chart(event.surface_index, event.area)
    end
  end
end)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Post Reply

Return to “Ideas and Requests For Mods”