Home   Help Search Login Register  

Author Topic: Using "random" in multiplayer  (Read 1932 times)

0 Members and 1 Guest are viewing this topic.

Rocko Bonaparte

  • Guest
Using "random" in multiplayer
« on: 17 Jan 2005, 08:24:07 »
I got nailed with random today while testing out a multiplayer mission.  I have a script that will randomly position the start squad within certain boundaries.  It worked in singleplayer, so I began the multiplayer transition.

In multiplayer, I've discovered that each human member of the squad is randomly positioned elsewhere.  This positioning code applies itself proportionately to all units in its list to affect.  I know that works since I have some objects nearby that aren't part of the group, yet they still are positioned near the squad leader.  The AI units are still by the squad leader.

I read about the consequences of random on multiplayer machines.  Specifically, the value is different when run on all computers.  I thought I had this covered--the randomizer is only called on the server because the callee script has "?!local Server : exit" preceeding all other code.

So all I can think of is that the server is allowing the clients to randomly generate the values as they are assigned on the server.  This is very strange, and hopefully incorrect.  I want to know if there's a decent workaround.

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #1 on: 19 Jan 2005, 07:26:43 »
I figured out that I have a more fundamental problem with the local server test.  I've made a gamelogic with text="Server" and I saved it into the mission.sqm.  The init.sqs doesn't seem to evaluate this test to true.  However, if I move the server code into another script, create a trigger, and have the trigger execute the script if "local Server" then that works correctly.  There's some problems with that associated with the delay involved.  

Shouldn't I be able to test if I'm running init.sqs on the server, or is that something special???

Offline Artak

  • The old beanbag shaker
  • Former Staff
  • ****
  • You want to talk about it, yes?
    • OFP Team Finlanders
Re:Using "random" in multiplayer
« Reply #2 on: 19 Jan 2005, 09:03:13 »
?local test should work in init.sqs as well.
Maybe post a commented version of your init.sqs here and we'll try to crack the bug?
Not all is lost.

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #3 on: 19 Jan 2005, 18:03:36 »
I'm not at that computer right now, so I can't upload it.  There may have been a consistency problem with the PBO I was testing.  I have two computers at home that I'll try to use for testing tonight before I post the code.

The original problem I had was that I was defining the Server using an init field:

init="Server = this";

Which was silly, I now use:

text="Server";

I'm confused why it wouldn't have worked in the init.sqs file--I have other things named in the mission that I access in the init.sqs.  Hence, my suspicion of my test host running an outdated pbo.

Offline Artak

  • The old beanbag shaker
  • Former Staff
  • ****
  • You want to talk about it, yes?
    • OFP Team Finlanders
Re:Using "random" in multiplayer
« Reply #4 on: 19 Jan 2005, 21:03:29 »
what? where? how? huh?

You know that the Server is just a gamelogic object named as server, don't you? you shouldn't have init="Server = this"; or text="Server"; anywhere. Just a gamelogic named as server.
This is because gamelogic objects are local to server.. so you could name the gamelogic to anything really and have ?local mygamelogic, but for the sake of keeping things understandable it's usually named as server.
Not all is lost.

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #5 on: 20 Jan 2005, 03:03:11 »
I thought the way to "name" things was to give them a text="name"; field for their entry in the mission.sqm.  Is it more trivial?

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #6 on: 20 Jan 2005, 04:44:56 »
It looks like the 'text="Server";' thing is working correctly; I had it hint if it was the server, and it did--the client didn't print anything.  However, I have made poor assumptions about what is communicated across machines.  This has all been very strange.

To recap: I have a script that repositions all the players using a random base position.  I use this on the playable squad, and all AI-controlled players will be repositioned together.  If the server is also a player, that player is repositioned with them.  However, all other human players are not repositioned.  This happens if I run the code in init.sqs.

Now let's say I create a trigger in the mission that immediately evaluates to true when the mission starts.  When activated, it calls the repositioning code.  It tests to make sure it only runs on the server.  This code moves the whole squad--human and AI--together correctly.  However, it takes a moment to activate, which makes it seem like we're being warped from one place to another in the game.

So how does this all work anyhow?  I believe I can use public variables to get the updated positions for each player.  It's tedious, but I think it's necessary, and I'll deal with it.  However, what about:

- Updated enemy waypoints
- Manual orders to enemy units
- event handles for weapon firing placed on all players

If this all runs in the init.sqs, and I have it only run on the server, am I in trouble?  That code is much harder to test.

Edit: I just discovered the magic of public variables, and it still doesn't make any sense.  What seems to happen is the positions of clients just cannot be changed with the code I have.  One problem is I don't really know where to use publicvariable -- does it go up top and get declared on client and servers?  Do I call it every time I change a noteworthy variable?  I don't really know.  Let me just paste a ton of code:

Code: [Select]
;publicvariable "rescue_pos1_x"
;publicvariable "rescue_pos1_y"
;publicvariable "rescue_pos2_x"
;publicvariable "rescue_pos2_y"
;publicvariable "rescue_pos3_x"
;publicvariable "rescue_pos3_y"
;publicvariable "rescue_pos4_x"
;publicvariable "rescue_pos4_y"
;publicvariable "rescue_pos5_x"
;publicvariable "rescue_pos5_y"
;publicvariable "rescue_pos6_x"
;publicvariable "rescue_pos6_y"
;publicvariable "rescue_pos7_x"
;publicvariable "rescue_pos7_y"
;publicvariable "rescue_pos8_x"
;publicvariable "rescue_pos8_y"
;publicvariable "rescue_pos9_x"
;publicvariable "rescue_pos9_y"
;publicvariable "rescue_pos10_x"
;publicvariable "rescue_pos10_y"
;publicvariable "rescue_pos11_x"
;publicvariable "rescue_pos11_y"

;publicvariable "prepared"

; Have one pilot dead (nonplayable)
DeadPilot setDammage 1.0

; Start up the chopper
(crew RescueChopper select 0) action ["Engine on", RescueChopper]

?!local Server: goto "ClientCode"

; For transmitting all updated player positions to the clients
; Arrays cannot be transmitted, so we have to be very tedious about this
rescue_pos1_x = 0
rescue_pos1_y = 0
rescue_pos2_x = 0
rescue_pos2_y = 0
rescue_pos3_x = 0
rescue_pos3_y = 0
rescue_pos4_x = 0
rescue_pos4_y = 0
rescue_pos5_x = 0
rescue_pos5_y = 0
rescue_pos6_x = 0
rescue_pos6_y = 0
rescue_pos7_x = 0
rescue_pos7_y = 0
rescue_pos8_x = 0
rescue_pos8_y = 0
rescue_pos9_x = 0
rescue_pos9_y = 0
rescue_pos10_x = 0
rescue_pos10_y = 0
rescue_pos11_x = 0
rescue_pos11_y = 0

; When 1, all the initial conditions for random positioning have been prepared
prepared = 0;
publicvariable "prepared"

VictoryConditions = 0
_crash_site = [0.0, 0.0]

randomly_position = preprocessFile "randomly_position.sqf"
fired_marker = preprocessFile "fired_marker.sqf"

marker_fired = 0
marker_shooter = objnull;

;_marker_fired = [[0, 1, 2, 3, "Flare"]] call fired_marker
Rescue1 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue2 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue3 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue4 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue5 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue6 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue7 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue8 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue9 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue10 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]
Rescue11 addEventHandler ["fired", format[ { marker_fired = marker_fired + ([_this] call fired_marker), marker_shooter=_this select 0 }]]

; place an infantry squad near the crash site so the rescue squad doesn't get much breathing time.

_crash_site = [[Rescue1, Rescue2, Rescue3, Rescue4, Rescue5, Rescue6, Rescue7, Rescue8, Rescue9, Rescue10, Rescue11, HeliWreck, DeadPilot], CrashBoundTL, CrashBoundBR] call randomly_position

rescue_pos1_x = (getPos Rescue1) select 0
publicvariable "rescue_pos1_x"
rescue_pos1_y = (getPos Rescue1) select 1
publicvariable "rescue_pos1_y"

hint format["Rescue 1 pos %1 %2", rescue_pos1_x, rescue_pos1_y]

rescue_pos2_x = (getPos Rescue2) select 0
publicvariable "rescue_pos2_x"
rescue_pos2_y = (getPos Rescue2) select 1
publicvariable "rescue_pos2_y"
rescue_pos3_x = (getPos Rescue3) select 0
publicvariable "rescue_pos3_x"
rescue_pos3_y = (getPos Rescue3) select 1
publicvariable "rescue_pos3_y"
rescue_pos4_x = (getPos Rescue4) select 0
publicvariable "rescue_pos4_x"
rescue_pos4_y = (getPos Rescue4) select 1
publicvariable "rescue_pos4_y"
rescue_pos5_x = (getPos Rescue5) select 0
publicvariable "rescue_pos5_x"
rescue_pos5_y = (getPos Rescue5) select 1
publicvariable "rescue_pos5_y"
rescue_pos6_x = (getPos Rescue6) select 0
publicvariable "rescue_pos6_x"
rescue_pos6_y = (getPos Rescue6) select 1
publicvariable "rescue_pos6_y"
rescue_pos7_x = (getPos Rescue7) select 0
publicvariable "rescue_pos7_x"
rescue_pos7_y = (getPos Rescue7) select 1
publicvariable "rescue_pos7_y"
rescue_pos8_x = (getPos Rescue8) select 0
publicvariable "rescue_pos8_x"
rescue_pos8_y = (getPos Rescue8) select 1
publicvariable "rescue_pos8_y"
rescue_pos9_x = (getPos Rescue9) select 0
publicvariable "rescue_pos9_x"
rescue_pos9_y = (getPos Rescue9) select 1
publicvariable "rescue_pos9_y"
rescue_pos10_x = (getPos Rescue10) select 0
publicvariable "rescue_pos10_x"
rescue_pos10_y = (getPos Rescue10) select 1
publicvariable "rescue_pos10_y"
rescue_pos11_x = (getPos Rescue11) select 0
publicvariable "rescue_pos11_x"
rescue_pos11_y = (getPos Rescue11) select 1
publicvariable "rescue_pos11_y"

prepared = 1
publicvariable "prepared"

;hint format["prepared is %1", prepared]

; The first responders will be randomly placed in a diagonal direction from the crash site.
_rand_x = random 2
_rand_x = _rand_x - (_rand_x mod 1)  
_rand_y = random 2
_rand_y = _rand_y - (_rand_y mod 1)  
_responders_pos = [((_crash_site select 0) + 100.0 * _rand_x - 200.0), ((_crash_site select 1) + 100.0 * _rand_y - 200.0)]
{_x setPos _responders_pos} forEach units FirstResponders

; Initial orders
; Move all the SOD waypoints to the crash site
[CaptureCar, 2] SetWPPos _crash_site
[CaptureCrew, 2] SetWPPos _crash_site
[PrisonerCar, 1] SetWPPos _crash_site
[FirstResponders, 1] SetWPPos _crash_site
[ProwlingShilka, 1] SetWPPos _crash_site
[Mi17_Spotter, 1] SetWPPos _crash_site

[North_Squad, 1] SetWPPos [(_crash_site select 0) + random(200) - 100.0, (_crash_site select 1) + random(200) - 100.0]
[South_Squad, 1] SetWPPos [(_crash_site select 0) + random(200) - 100.0, (_crash_site select 1) + random(200) - 100.0]
[East_Squad, 1] SetWPPos [(_crash_site select 0) + random(200) - 100.0, (_crash_site select 1) + random(200) - 100.0]
[West_Squad, 1] SetWPPos [(_crash_site select 0) + random(200) - 100.0, (_crash_site select 1) + random(200) - 100.0]

; Pick a "Sasser Squad." Randomly pick from one or more of all the controlled East troops in this mission,
; and override their orders with orders to attack the rescue group at all times.  This means it will be updated
; every time the command loop finishes.  It's hardly fair, but tons more fun.  We'll use the SOD waypoints for all
; units -- this allows any boarded APCs to unload before their sassing begins
_array_picker = random 10
_array_picker = _array_picker - (_array_picker mod 1)
_sasser_waypoint = [[CaptureCar, 2], [CaptureCrew, 2], [PrisonerCar, 1], [FirstResponders, 1], [ProwlingShilka, 1], [North_Squad, 1], [South_Squad, 1], [East_Squad, 1], [West_Squad, 1], [Mi17_Spotter, 1]] select _array_picker

; Commander's loop.  This code acts like a commander making decisions to all the east's resources available for
; tracking and killing this opponent.
#CommandLoop

; Has a flare been detected?  If so, send a helicopter towards it.
if(marker_fired > 0) then { Mi17_flare_hunter move position marker_shooter, Mi17_flare_hunter setCombatMode "RED", Mi17_flare_hunter commandTarget marker_shooter}

; Updating the sasser squads to the exact coordinates of the downed crew
_sasser_waypoint SetWPPos (getPos leader Downed_Squad)
[FirstResponders, 1] SetWPPos (getPos leader Downed_Squad)

~5
; Check if the victory conditions are met:
; 1. All downed crew that are alive are in the chopper
; 2. The chopper is near the landing base
#VictoryCheck
; Check if the victory conditions are met:
; 1. All downed crew that is alive is in the chopper
; 2. They chopper is near the landing base
#VictoryCheck
VictoryConditions = 0
?alive Rescue1 && !(Rescue1 in RescueChopper) : goto "CommandLoop"
?alive Rescue2 && !(Rescue2 in RescueChopper) : goto "CommandLoop"
?alive Rescue3 && !(Rescue3 in RescueChopper) : goto "CommandLoop"
?alive Rescue4 && !(Rescue4 in RescueChopper) : goto "CommandLoop"
?alive Rescue5 && !(Rescue5 in RescueChopper) : goto "CommandLoop"
?alive Rescue6 && !(Rescue6 in RescueChopper) : goto "CommandLoop"
?alive Rescue7 && !(Rescue7 in RescueChopper) : goto "CommandLoop"
?alive Rescue8 && !(Rescue8 in RescueChopper) : goto "CommandLoop"
?alive Rescue9 && !(Rescue9 in RescueChopper) : goto "CommandLoop"
?alive Rescue10 && !(Rescue10 in RescueChopper) : goto "CommandLoop"
?alive Rescue11 && !(Rescue11 in RescueChopper) : goto "CommandLoop"
VictoryConditions = 1

goto "CommandLoop"

#ClientCode

;@prepared == 1

hint format["Rescue1 position %1 %2", rescue_pos1_x, rescue_pos1_y]

@prepared == 1

Rescue1 setPos [rescue_pos1_x, rescue_pos1_y]
Rescue2 setPos [rescue_pos2_x, rescue_pos2_y]
Rescue3 setPos [rescue_pos3_x, rescue_pos3_y]
Rescue4 setPos [rescue_pos4_x, rescue_pos4_y]
Rescue5 setPos [rescue_pos5_x, rescue_pos5_y]
Rescue6 setPos [rescue_pos6_x, rescue_pos6_y]
Rescue7 setPos [rescue_pos7_x, rescue_pos7_y]
Rescue8 setPos [rescue_pos8_x, rescue_pos8_y]
Rescue9 setPos [rescue_pos9_x, rescue_pos9_y]
Rescue10 setPos [rescue_pos10_x, rescue_pos10_y]
Rescue11 setPos [rescue_pos11_x, rescue_pos11_y]

What I've tested is having server as Rescue2, and client as Rescue3.  On both machines, it will claim Rescue1 is at the default location, not one generated by the random placement function.  If I make the server Rescue1, it will be randomly assigned, and both machines will print the correct location.  If make the client Rescue1, it will be at the default location and both servers will agree.  It just seems like I can't make the client budge.  How the hell can that be?
« Last Edit: 20 Jan 2005, 07:08:25 by Rocko Bonaparte »

Offline Artak

  • The old beanbag shaker
  • Former Staff
  • ****
  • You want to talk about it, yes?
    • OFP Team Finlanders
Re:Using "random" in multiplayer
« Reply #7 on: 20 Jan 2005, 10:18:54 »
That's funny. I always name objects and units by typing the name I want in their name fields.  :)

publicvariable must be used everytime you change a variable on server and want clients to get the right value too. Like this:

Code: [Select]
clientscontinue = false
randpos = 0

;move all clients to 'wait' bookmark
?!local server: goto "wait"

;pick a random value from 0 to 100 to _rand variable on server only
?local server: randpos = random 100

;send the value of 'randpos' to all clients.
publicvariable "randpos"

;set a variable to true
clientcontinue = true

;publicvariable the value of the boolean variable
publicvariable "clientcontinue"

;wait bookmark is where clients go to wait for the server to pick the random
;and publicvariable it
#wait

;have everybody wait for the variable 'clientcontinue' to become true until continuing.
;this way everyone will be synced again
@clientcontinue

;move all units in grp1 group to one of four marker positions depending on what
;value randpos variable got
?randpos < 25: "_x setpos getpos {marker1}" foreach units grp1; goto "continue"
?randpos >= 25 && randpos < 50: "_x setpos getpos {marker2}" foreach units grp1; goto "continue"
?randpos >= 50 && randpos < 75: "_x setpos getpos {marker3}" foreach units grp1; goto "continue"
?randpos >= 75: "_x setpos getpos {marker4}" foreach units grp1; goto "continue"
hint "error. randpos value incorrect."

#continue


It would be a lot easier to just group (F2) the groups to markers. Grouping to markers acts as a random placement.
Not all is lost.

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #8 on: 20 Jan 2005, 18:32:42 »
The random placement script I have is moving more than a group.  It's also moving a crashed helicopter and a dead pilot.   Moving just the group together with a waypoint may be a little cleaner, although I think it is going around a conceptual problem I'm having here.

That last message was very verbose, so here's the current problem:
I print the position of Rescue1 on both machines.
1. If Rescue1 is AI, it will get the random position.and server will be near Rescue1.
2. If Rescue1 is server, it will get a random position and the AI players will be near it.  
In all cases, the computers report the same position.  In all cases, the client stays at the default location.  So the strange part is the server moves Rescue1, the client acknowledges it, and they both report the default position.  This I don't understand--it's as if the server refuses to move a unit if it's controlled by a player.

Rocko Bonaparte

  • Guest
Re:Using "random" in multiplayer
« Reply #9 on: 21 Jan 2005, 05:21:33 »
I made a test mission just to simplify the situation.  It seems that if I try to reposition a client human player inside a function (sqf), it just plain won't work.

Here's the simplified init.sqs:
Code: [Select]
randomly_position = preprocessFile "randomly_position.sqf"

?!local Server : goto "ClientCode"
#Server_Code

_crash_site = [[Test1, Test2], BoundTL, BoundBR] call randomly_position

xpos = getpos Test2 select 0
ypos = getpos Test2 select 1
publicvariable "xpos"
publicvariable "ypos"

positioned = 1
publicvariable "positioned"

exit

#ClientCode

hint format["ClientCode _positioned is %1", positioned]

@positioned == 1

hint format["Client %1 %2", xpos, ypos]

Test2 setPos [xpos, ypos]

exit

And here's that randomly_position function:
Code: [Select]
private ["_tomove_list", "_tl_randombox", "_br_randombox", "_width", "_height", "_base_x",
      "_base_y", "_first_x", "_first_y", "_diff_x", "_diff_y", "_randomized_pos"];

_tomove_list = _this select 0;
_tl_randombox = _this select 1;
_br_randombox = _this select 2;

// Get a random position based on the random box bounds
_width = ((getpos _br_randombox) select 0) - ((getpos _tl_randombox) select 0);
_height = ((getpos _tl_randombox) select 1) - ((getpos _br_randombox) select 1);

_base_x = ((getpos _tl_randombox) select 0) + random(_width);
_base_y = ((getpos _br_randombox) select 1) + random(_height);

// We now have randomized x and y coordinates.  Take the first unit in the group, use it as a basis.
// Position this basis at that exact position and then move all the other units relative to it.
_first = (_tomove_list select 0);
_first_x = getpos _first select 0;
_first_y = getpos _first select 1;

_diff_x = _base_x - _first_x;
_diff_y = _base_y - _first_y;

// Position all units in the group based on the random point and relative to the
// first one that was mentioned in the list.
{
   _x setpos [(getpos _x select 0) + _diff_x, (getpos _x select 1) + _diff_y];
} forEach _tomove_list;

// Return the coordinates that were randomly assigned
_randomized_pos = [_base_x, _base_y];



hint format["Test2 (%1,%2)", getpos (_tomove_list select 1) select 0, getpos (_tomove_list select 1) select 1];


_randomized_pos
If Test2 is AI, it will be repositioned.  If Test2 is human, it will ALwAYS report back the position I placed it at in the mission editor; human players just won't budge.

I want to know why this doesn't work.  In the meantime, I will move the squad using that group waypoint trick, but it's too bad I can't get it to work any other way.

Offline Gnat

  • Addons Depot
  • Former Staff
  • ****
  • I Bite!
    • Gnats
Re:Using "random" in multiplayer
« Reply #10 on: 28 Jan 2005, 03:18:35 »
 ??? Human players are "randomly" placed (relative to 30+ invisible map marker) at the beginning of a CTI mission ....... so it has to work somehow .....