Page 1 of 1

Multiplayer Compatible Help

Posted: Tue Oct 13, 2015 5:46 pm
by TheSAguy
I don't play multiplayer myself and can't test my mod to see if it is, I was told that "game.player" is not multiplayer compatible and was hoping I could get some help to fix this.

I use "game.player" a few places in my Mod.
The attached mod has not yet been released. It's also here on GitHub

In "Natural_Evolution_Expansion_5.0.0" it is used in the below code:
If the player has build a Rocket Silo, I have the biters attack them by using: (so "game.player" is used twice here)

Code: Select all

			if global.RocketSiloBuilt > 0 then
				---- Attack the player, since you have a silo built
				game.player.surface.set_multi_command({type=defines.command.attack,target=game.player.character,distraction=defines.distraction.by_enemy},Enemy_Count)

			end 
I also use it to print the below message when the player builds a Rocket Silo.

Code: Select all

game.player.print("WARNING!")


In "Natural_Evolution_Enemies_5.0.0" it is used in the below code: (so "game.player" is used twice here)
If the player kills a spawner, I have the nearby enemies attack him:

Code: Select all

if event.entity.type == "unit-spawner" then
game.player.surface.set_multi_command({type=defines.command.attack,target=game.player.character,distraction=defines.distraction.by_enemy},(20+math.floor(game.evolution_factor*100)))
end



I'm not sure if the same goes for "game.players"
In "Natural_Evolution_Buildings_5.0.0" it is used in the below code:

Code: Select all

game.on_event(defines.events.on_tick, function(event)
	--	Evolution_MOD
	if event.tick % update_com_count == 0 then
		for index, player in ipairs(game.players) do
			if player.character then
				UpdateUnitsCommands(index)		
			end
		end
	end
end)
Thanks.

Re: Multiplayer Compatible Help

Posted: Wed Oct 14, 2015 3:05 pm
by Outsider
You need to change all references to game.player, to a specific player object.. there are several ways to do that depending on where and how your functions are called.

Generally speaking if you are initializing any conflagration options specific to players you'd loop the game.players, you usually have to use this for any player specific code that runs inside the on_tick or on_init/load events.

Then you have events that come with a player_index, ex : on_built_entity, on_put_item, on_preplayer_mined_item, on_player_created, on_gui_click...

these should cover all the event you might be using in your mods, so let's say in one of your functions that handles the rocket silo, you'd have to change the function parameters to accept a player/player_index and then use that player object in your calls :

Code: Select all

function handleSilos(index) 
         player = game.players[index]
         if global.RocketSiloBuilt[index] > 0 then
            ---- Attack the player, since you have a silo built
                   player.surface.set_multi_command({type=defines.command.attack,target=game.player.character,distraction=defines.distraction.by_enemy},Enemy_Count)

         end
end
Note how you also should consider setting your globals scope per player or per force depending on what your mod does..

Your Enemies mod however might be a bit trickier - i haven't really looked at the full code, just your sample here, but i assume you are using the on_entity_died event which has no reference to a specific player. so not sure what you can do here to make it mp friendly other than looping and attacking all players, another option here tho would be to look for a nearby player based on the killed spawner position and a radius and that would give you the closest player who "probably" killed that spawner - not ideal but could work?

another thing to note that i ran into is how the player index is handled, i believe right now it depends on the join order and is tied to the player name.. so for instance if a player joined a game and later on disconnected and came back using the same name, he'd still have the same index.. if that player changed their name they'd have a new index, something to consider when doing mp work.. i personally just use the player indexes for configuration scope, but using the player name could be a viable option too.. haven't really play tested on mp enough to make up my mind on this.

Re: Multiplayer Compatible Help

Posted: Thu Oct 15, 2015 7:16 pm
by TheSAguy
Outlander,

Thanks for your input.

For the Enemies Mod, Let's say I want to cycle through the players and attack them all when a spawner is killed, would I do it this way:

Code: Select all

	
if event.entity.type == "unit-spawner" then
        for i = 1, #game.players, 1 do
	      player = game.players[i]
	      player.surface.set_multi_command({type=defines.command.attack,target=player.character,distraction=defines.distraction.by_enemy},20)
      end
end
I'm still looking into your answer on the silo's. So you created a function, but now I need to figure out how to call it :)

Re: Multiplayer Compatible Help

Posted: Thu Oct 15, 2015 7:19 pm
by jorgenRe
This is how i do it to cycle through all the players to write in the console for all:

Code: Select all

function message(mes)
  for i, player in ipairs(game.players) do
    player.print(mes)
  end
end
The player.print(mes) can be changed to whatever you want to do with each player.

Re: Multiplayer Compatible Help

Posted: Fri Oct 16, 2015 8:28 pm
by Outsider
TheSAguy wrote:Outlander,

Thanks for your input.

For the Enemies Mod, Let's say I want to cycle through the players and attack them all when a spawner is killed, would I do it this way:

Code: Select all

	
if event.entity.type == "unit-spawner" then
        for i = 1, #game.players, 1 do
	      player = game.players[i]
	      player.surface.set_multi_command({type=defines.command.attack,target=player.character,distraction=defines.distraction.by_enemy},20)
      end
end
I'm still looking into your answer on the silo's. So you created a function, but now I need to figure out how to call it :)
i was thinking more like finding player entities around the spawner that died and sending attacks at those players or maybe one of them.. but it's up to you if you want to send the attack to all players on the map.

the code below is untested so it might not work right away, it basically searches for player entities in a specific radius around a dead spawner and sends attacks to all the players found.

Code: Select all

game.on_event(defines.events.on_entity_died, function(event)
   if event.entity.type == "unit-spawner" then
       local surface = game.get_surface(0) -- i'm really not sure how surfaces work between different forces and entities so i mostly use this.
       local radius = 25
       local pos = event.entity.position
       local area = {{pos.x - radius, pos.y - radius}, {pos.x + radius, pos.y + radius}}

       -- find nearby players
       local players = surface.find_entities_filtered{area=area, type="player"}

        -- send attacks to all nearby players
        for i,player in pairs(players) do
            surface.set_multi_command({type=defines.command.attack,target=player.character,distraction=defines.distraction.by_enemy},(20+math.floor(game.evolution_factor*100)))
        end

   end
end)
as for the silos, you'd call that function in an on_entity_built event like so :

Code: Select all

game.on_event(defines.events.on_built_entity, function(event)
    local index = event.player_index
    if index then
        handleSilos(index)
    end
end)
you obviously can do checks here to see if the entity being built is actually a rocket silo.. you don't even need a function you can have the whole code right on the event callback itself.