random numbers .....

The place to post your WML questions and answers.

Moderators: Forum Moderators, Developers

Forum rules
  • Please use [code] BBCode tags in your posts for embedding WML snippets.
  • To keep your code readable so that others can easily help you, make sure to indent it following our conventions.
Post Reply
Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

random numbers .....

Post by Mabuse » November 1st, 2008, 7:26 pm

hi,

i have a serious problem with random numbers in multiplayer -

well, if i generate random numbers in wml, that determine if a artilery rocket hits for example -
will the result be the same for all players, or will every player make a separate roll on that event and thus the results can be completely different ?

i have some refernces that the latter may happen, but i thought i may ask the wml-specialists before i competely think about a new solution
The best bet is your own, good Taste.

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 1st, 2008, 7:40 pm

this is the relevant code that is used ...

Code: Select all


....

# Does the shot hit?
{RANDOM 1..100}

[if]
	{BOB_CONDITION random less_than $chance_to_hit}
	[then]

....


no you see nothing special or something, and the code itself works fine,
its just that in mjultiplayer match, the reuslt can be completely different, one player see both missiles hit, the other get a 1 miss/1hit ... and that mess up the game

any explanations for that
im using version 1.4.5


else i may change the whole approach (dont use random), although im quite happy with the thing as it is atm, since its cooler to get a full random damage, instead of calculation of the damage depending on terrain (use terrain as a factor to calculate damage)

but if it dont work it dont work -

the question i have :

SHOULD it work, or is it really different for the players by design
The best bet is your own, good Taste.

User avatar
zookeeper
WML Wizard
Posts: 9740
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: random numbers .....

Post by zookeeper » November 1st, 2008, 8:15 pm

Use rand= for random in MP, not random=.

And make sure random is called the same number of times for all clients (so don't use it in select events for instance).

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 2nd, 2008, 12:39 am

zookeeper wrote:Use rand= for random in MP, not random=.

And make sure random is called the same number of times for all clients (so don't use it in select events for instance).

um, does the RANDOM THING Macro not "automatically" use rand ;)

Code: Select all

#define RANDOM THING
    # Macro to quickly pick a random value (in the $random variable, to avoid
    # cluttering up savegames with such temporary variables).
    [set_variable]
        name=random
        rand={THING}
    [/set_variable]
#enddef
or what do you mean ?

i dont fully understand your comment, so the code i have should theoretically work ?

(since {RANDOM 1..100} is used)
The best bet is your own, good Taste.

User avatar
zookeeper
WML Wizard
Posts: 9740
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: random numbers .....

Post by zookeeper » November 2nd, 2008, 9:24 am

Mabuse wrote:
zookeeper wrote:Use rand= for random in MP, not random=.

And make sure random is called the same number of times for all clients (so don't use it in select events for instance).

um, does the RANDOM THING Macro not "automatically" use rand ;)

Code: Select all

#define RANDOM THING
    # Macro to quickly pick a random value (in the $random variable, to avoid
    # cluttering up savegames with such temporary variables).
    [set_variable]
        name=random
        rand={THING}
    [/set_variable]
#enddef
or what do you mean ?

i dont fully understand your comment, so the code i have should theoretically work ?

(since {RANDOM 1..100} is used)
Oh, right. Yes, it should work.

Rhuvaen
Inactive Developer
Posts: 1272
Joined: August 27th, 2004, 8:05 am
Location: Berlin, Germany

Re: random numbers .....

Post by Rhuvaen » November 2nd, 2008, 1:01 pm

It depends on the event context. In MP it's important whether the type of event is called on all clients simultaneously (giving a different result on each), or on one client only who then passes the result to all other clients. The second type is the events that you can use random in effectively.

Events that get called by all clients are usually events that happen due to some changes that are not directly effected by a particular player's moves. I'm not up to date with the current state of the wml engine but among those are certainly:
- start (actually I'm not certain about this one...)
- turn x
- side turn
and probably all the victory/defeat/time over events as well as turn refresh

Events that get called on one client only are
- moveto
- all the attack events
- recruit
- capture
- menu item x
Really anything that happens between the time a player's orbs turn green and when he ends the turn.

Of course, not all of these events are sent over to the other clients - and you shouldn't use these
- select
and I also wouldn't count on sighted.

The same that applies to random applies to message options, btw (because the client yields a result - through user action, not randomly).

It would probably be great to keep a list of events that are suitable for options and random in MP somewhere on the wiki...

User avatar
zookeeper
WML Wizard
Posts: 9740
Joined: September 11th, 2004, 10:40 pm
Location: Finland

Re: random numbers .....

Post by zookeeper » November 2nd, 2008, 2:16 pm

Rhuvaen wrote: Events that get called on one client only are
- moveto
- all the attack events
- recruit
- capture
- menu item x
Really anything that happens between the time a player's orbs turn green and when he ends the turn.
Hmh? All of those should trigger just fine on other clients too. The move/action taken is transmitted to the other clients, and they then trigger the events accordingly as well.

However, I think you shouldn't use random in capture events because you can undo capturing a village, which would allow you to make the RNG's go out of sync.

The only events I think are inherently unsafe would be select, and probably sighted as well.

And yeah, message options shouldn't be used except in events which are triggered directly by player actions: attack, moveto, etc. New turn, side turn and such events are triggered by each client separately, so client A won't get any information on what client B might have chosen in a menu presented to him in a side turn event.

Rhuvaen
Inactive Developer
Posts: 1272
Joined: August 27th, 2004, 8:05 am
Location: Berlin, Germany

Re: random numbers .....

Post by Rhuvaen » November 2nd, 2008, 2:37 pm

zookeeper wrote:Hmh? All of those should trigger just fine on other clients too. The move/action taken is transmitted to the other clients, and they then trigger the events accordingly as well.
I meant to say that these events trigger on one client initially, and the results (of random/options) are used on all the other clients to resolve that event. So yes, that is the list of events that should be safe to use. Perhaps that wasn't so clear.

Oh, and I really hate that the edit and quote buttons are right next to each other... :evil:

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 2nd, 2008, 9:04 pm

well, -

to specify this:

a player right clicks on an enemy unit (within a certain range of a certain own unit) and "SELECTS" a MENU OPTION that allows him to fire an artilery rocket. a rocket is spawned and fake moved to the other unit, and then random is called to determine if it hits or not.

after all you wrote i understand it like this is one of the "bad random" approaches which may cause OOS.
The best bet is your own, good Taste.

User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: random numbers .....

Post by Sapient » November 2nd, 2008, 9:12 pm

menu rightclick events are fired on both clients
(synchronozied)
thus it should not cause an OOS
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 3rd, 2008, 3:21 pm

but it does -

the results of the random number are different for the players
The best bet is your own, good Taste.

Rhuvaen
Inactive Developer
Posts: 1272
Joined: August 27th, 2004, 8:05 am
Location: Berlin, Germany

Re: random numbers .....

Post by Rhuvaen » November 3rd, 2008, 9:49 pm

Mabuse wrote:to specify this:

a player right clicks on an enemy unit (within a certain range of a certain own unit) and "SELECTS" a MENU OPTION ...
How is the certain own unit defined? Perhaps the clients fire different events?

Really, this is quite complicated and we need to see some code...

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 4th, 2008, 3:21 pm

ok, here is the full code
it is basically taken from BOB's MechWars-Era
(with slightest modifications, which doesnt change anything about the principle, just add some sounds, add check for new terrain types (and lit mushroom) and some not used stuff was cut out ---> allow to load different numbers of rockets)

the action is started with
GE_LOAD_ROCKET
which makes a rocket ready to shoot (right click on own unit an slect option)

then
GE_SHOOT_ROCKET
which is activated by right click on enemy unit (within a certain range (4 tiles in this case) of the unit which loaded the rocket

the random number is then called by
GE_ROCKET_STUFF




Code: Select all

#define GE_TERRAIN_CHECKER TERRAIN LETTERS 
[if]
	[have_location]
		x,y=$x1,$y1
		terrain={LETTERS}
	[/have_location]
	[then]
		{VARIABLE_OP chance_to_hit format $unit.defense.{TERRAIN}}
	[/then]
[/if]
#enddef

#define GE_TERRAIN_CHECKER_2 TERRAIN_1 TERRAIN_2 LETTERS 
[if]
	[have_location]
		x,y=$x1,$y1
		terrain={LETTERS}
	[/have_location]
	[then]
	
		[if]
		{BOB_CONDITION unit.defense.{TERRAIN_1} less_than_equal_to $unit.defense.{TERRAIN_2}}
			[then]
				{VARIABLE_OP chance_to_hit format $unit.defense.{TERRAIN_1}}
			[/then]
			[else]
				{VARIABLE_OP chance_to_hit format $unit.defense.{TERRAIN_2}}
			[/else]
		[/if]

	[/then]
[/if]
#enddef

#define GE_DEFENSE_CALCULATOR

# Basic Terrain
{GE_TERRAIN_CHECKER deep_water "Wo"}
{GE_TERRAIN_CHECKER shallow_water "Ww,Ww^Vm"}
{GE_TERRAIN_CHECKER swamp_water "Ss,Ss^Vm"}
{GE_TERRAIN_CHECKER grassland "Gg,Ggf,Gs ,Rd,Re,Rr,Rp,Re^Gvs,Gg^Wm"}
{GE_TERRAIN_CHECKER sand "Dd,Ds,Dd^Dc"}
{GE_TERRAIN_CHECKER forest "Gg^Fet,Gs^Fp,Gs^Ft"}
{GE_TERRAIN_CHECKER hills "Dd^Dr,Hh"}
{GE_TERRAIN_CHECKER mountains "Md,Mm"}
{GE_TERRAIN_CHECKER village "Aa^Vea,Gg^Ve,Aa^Vha,Gg^Vh,Gs^Vht"}
{GE_TERRAIN_CHECKER castle "Ce,Ch,Cv,Cud,Chr,Ke,Kh,Kv,Kud,Khr"}
{GE_TERRAIN_CHECKER cave "Uu,Uu^Ii"}
{GE_TERRAIN_CHECKER tundra "Ai,Aa"}
{GE_TERRAIN_CHECKER canyon "Qxu,Ql,Qlf"}
{GE_TERRAIN_CHECKER cavewall "Mm^Xm,Md^Xm,Xu,Xv"}
{GE_TERRAIN_CHECKER fungus "Uu^Uf,Re^Uf,Uu^Ufi"}
{GE_TERRAIN_CHECKER moon "Zze,Zzj"}

# Bridges
{GE_TERRAIN_CHECKER_2 deep_water grassland "Wo^Bw|,Wo^Bw/,Wo^Bw\"}
{GE_TERRAIN_CHECKER_2 shallow_water grassland "Ww^Bw\,Ww^Bw|,Ww^Bw/"}
{GE_TERRAIN_CHECKER_2 swamp_water grassland "Ss^Bw\,Ss^Bw|,Ss^Bw/"}

# Castles
{GE_TERRAIN_CHECKER_2 shallow_water castle "Chw,Khw"}
{GE_TERRAIN_CHECKER_2 swamp_water castle "Chs,Khs"}

# Villages
{GE_TERRAIN_CHECKER_2 swamp_water village "Ss^Vhs"}
{GE_TERRAIN_CHECKER_2 cave village "Uu^Vu,Uu^Vud"}
{GE_TERRAIN_CHECKER_2 hills village "Hh^Vhh,Ha^Vhha"}
{GE_TERRAIN_CHECKER_2 mountains village "Mm^Vhh"}

# Hills - cave,sand,snow,moon
{GE_TERRAIN_CHECKER_2 cave hills "Uh,Uh^Ii"}
{GE_TERRAIN_CHECKER_2 sand hills "Hd"}
{GE_TERRAIN_CHECKER_2 tundra hills "Ha"}
{GE_TERRAIN_CHECKER_2 moon hills "Zzf,Zzg,Zzk,Zzh,Zzl"}

# Mountains - moon
{GE_TERRAIN_CHECKER_2 moon mountains "Zzi,Zzm"}

# River-ford
{GE_TERRAIN_CHECKER_2 grassland shallow_water "Wwf"}

# Snow-forest
{GE_TERRAIN_CHECKER_2 tundra forest "Aa^Fpa"}
#enddef

#define GE_REMOVE_OLD_GUNNER
    [store_unit]
        [filter]
				[wml_filter]
					[variables]
						rocket_ready=2
					[/variables]
				[/wml_filter]
        [/filter]
        variable=old_gunner
    [/store_unit]

    {VARIABLE_OP old_gunner.variables.rocket_ready format 0}
    {VARIABLE_OP old_gunner.overlays add "none"}
    
    [unstore_unit]
        variable=old_gunner
    [/unstore_unit]
#enddef

#define GE_LOAD_ROCKET
[set_menu_item]
	id=Load_Rockets
	description="Load Artilery Rockets"
	image=misc/aim.png
	
	[show_if]
		[have_unit]
			x,y=$x1,$y1
			side=$side_number
			has_weapon="missile launcher"

			
			[wml_filter]
				attacks_left=1
			[/wml_filter]

			[not]
			[wml_filter]
				[variables]
					rocket_ready=2
				[/variables]
			[/wml_filter]
			[/not]			
		[/have_unit]
	[/show_if]	
	
	[command]
		{GE_REMOVE_OLD_GUNNER}
		{BOB_STORE_UNIT x=$x1 y=$y1 (
		{VARIABLE stored_unit.overlays halo/aim-overlay.png}
		{VARIABLE_OP stored_unit.variables.rocket_ready format 2}
		)}
	{PLAY_SOUND readyguns.wav}
	[/command]	
[/set_menu_item]
#enddef


#define GE_ROCKET_STUFF
{PLAY_SOUND missile2.wav}
[move_unit_fake]
	side=$side_number
	type="Rocket"
	x=$shooter.x,$x1
	y=$shooter.y,$y1
[/move_unit_fake]

[store_unit]
	[filter]
		x,y=$x1,$y1
	[/filter]
	variable=target
	kill=no
	[/store_unit]
	
# Does the shot hit?
{RANDOM 1..100}

[if]
	{BOB_CONDITION random less_than $chance_to_hit}
	[then]
	# Calculating Damage
	{VARIABLE_OP damage format 15}
	{VARIABLE_OP damage multiply $target.resistance.impact}
	{VARIABLE_OP damage divide 100}
	# Dealing Damage
	{VARIABLE_OP target.hitpoints add -$damage}
	{PLAY_SOUND medium-shipexplosion.wav}
	
	[animate_unit]
	flag=defend
	[filter]
		x=$x1
		y=$y1
	[/filter]
	[/animate_unit]

	[unstore_unit]
		variable=target
		text=$damage
		{COLOR_HARM}
	[/unstore_unit]
	[/then]
	
	[else]
		[unstore_unit]
		variable=target
		text=Missed
		{BOB_COLOR_GREY}
		[/unstore_unit]
	[/else]
[/if]
#enddef


#define GE_SHOOT_ROCKET	
[set_menu_item]
		id=Shoot_Missile
		description="Shoot Missile"
		image=misc/aim.png
		
		[show_if]
			[have_unit]
				x,y=$x1,$y1
			[/have_unit]
		[/show_if]
	
		[filter_location]
			[filter]
				has_weapon="missile launcher"
				side=$side_number
				
				[wml_filter]
					attacks_left=1
				[/wml_filter]	
			
					[wml_filter]
						[variables]
							rocket_ready=2
						[/variables]
					[/wml_filter]

				[not]
					[wml_filter]
						[variables]
							team_name=$unit.variables.team_name
						[/variables]
					[/wml_filter]
				[/not]
				
			[/filter]
			radius=4
		[/filter_location]
		
[command]

# Shooter

[store_unit]
	[filter]
		side=$side_number
		has_weapon="missile launcher"
		
		[wml_filter]
			attacks_left=1
		[/wml_filter]
		
		[not]
			[wml_filter]
				[variables]
					rocket_ready=0
				[/variables]
			[/wml_filter]
		[/not]
	[/filter]
	variable=shooter
	[/store_unit]

# --------------------------

# Chance-to-hit and damage

{GE_DEFENSE_CALCULATOR}

# If there is 2 shots fired
[if]
{BOB_CONDITION shooter.variables.rocket_ready greater_than 1}
	[then]
		{GE_ROCKET_STUFF}
		{GE_ROCKET_STUFF}
	[/then]
[/if]

# If there are 3 shots fired
[if]
{BOB_CONDITION shooter.variables.rocket_ready greater_than 2}
	[then]
		{GE_ROCKET_STUFF}
	[/then]
[/if]

# --------------------------
	
	[store_unit]
		[filter]
			x,y=$x1,$y1
		[/filter]
		variable=wounded
	[/store_unit]
	
	# Is target killed?		
	[if]
	{BOB_CONDITION wounded.hitpoints less_than_equal_to 0}
		# Yes
		[then]
			# subclause to check what level the unit is, and award XP accordingly
			[if]
				{BOB_CONDITION wounded.level greater_than_equal_to 1}
				[then]
					{VARIABLE_OP temp_xp format $wounded.level}
					{VARIABLE_OP temp_xp multiply 4}
					{VARIABLE_OP shooter.experience add $temp_xp}
				[/then]
				[else]
					{VARIABLE_OP shooter.experience add 4}
				[/else]
			[/if]
			
			[unstore_unit]
				variable=wounded
			[/unstore_unit]
		
			[kill]
				x,y=$x1,$y1
				animate=yes
			[/kill]
		[/then]
		# No
		[else]
			{VARIABLE_OP shooter.experience add $wounded.level}
			{VARIABLE_OP wounded.experience add $shooter.level}
			[unstore_unit]
				variable=wounded
			[/unstore_unit]
		[/else]
	[/if]
	
# --------------------------
	
{VARIABLE_OP shooter.variables.rocket_ready format 0}
{VARIABLE_OP shooter.overlays add "none"}
{VARIABLE_OP shooter.attacks_left add -1}
{VARIABLE_OP shooter.moves format 0}

			[unstore_unit]
			variable=shooter
			[/unstore_unit]
	
{CLEAR_VARIABLE damage}
{CLEAR_VARIABLE target}
{CLEAR_VARIABLE wounded}
{CLEAR_VARIABLE temp_xp}
{CLEAR_VARIABLE shooter}
[/command]
[/set_menu_item]
#enddef
The best bet is your own, good Taste.

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 4th, 2008, 3:34 pm

as said the alternative for the random check woudl be to use the chanche-to-hit/100 as a mulitplier for the damage (and the shots would always hit)

atm it looks like i need to change it, because i cant allow the current version longer to be used anyway since the use of the artilery may/will cause oos atm. (all games i played so far ended with serious oos, when artilery was involved - i just got in the last game behind this becaseu formerly i suspected some strabge teleport issue responsible for this, but thew last game i played showed clearly that something is wrong with the artilery (different hits for different players))
The best bet is your own, good Taste.

Mabuse
Posts: 2130
Joined: November 6th, 2007, 1:38 pm

Re: random numbers .....

Post by Mabuse » November 6th, 2008, 4:58 pm

btw, i maybe forgot to mention that within the SCN the function (or however they are named) are called via a pre-start event) -


[event]
name=prestart

# Right-click options
{GE_LOAD_ROCKET}
{GE_SHOOT_ROCKET}
[/event]



btw, since the code is mainly form Bobs mechwars era i strongly assume that mechwarsera is broken too in multiplayer -
anybody played mechwarsera in multiplayer (using rockets and stuff) and get or didnt get oos ?

of course you must always see the context, the shot itself wont cause oos, but if units are differently damaged and a unit is killed due to this earlier than in another ones game the oos will occur - this can be a completely different situation then.

so the test would/could be that a player fires rockets on another player and then both tell each other how many hits they achieved.

what i wanted to tell with this: noob comments a'la "i played mech wars once and when i shot the rocket i didnt get an oos" doesnt help here, instead i want a more detailed feedback on that matter - also only multiplayer is of interest here here

anyway i may set up a test for myself, but maybe anybody has expieriences with this already
The best bet is your own, good Taste.

Post Reply