OFPEC Forum

Editors Depot - Mission Editing and Scripting => OFP - Editing/Scripting Multiplayer => Topic started by: uiox on 14 Apr 2003, 10:52:05

Title: Some tips for addon scripter & mission designer
Post by: uiox on 14 Apr 2003, 10:52:05
Hi,

I have script some code for the use of dialogs with an addon, it was hard and I have found some tips:

- The event init call in config.cpp is only on server side. (not test on a stand alone server, only hosting)

- If you want initialize a variable you do like this :

If  (format ["%1", MyArray ] == "scalar bool array string 0xfcffffef")  then { MyArray = [] }

- You can build array with public variables like this:
For build it in server or client

If  (format ["%1", MyArray ] == "scalar bool array string 0xfcffffef")  then { MyArray = [] }
_obj = _this select 0
MyArray = MyArray + [_obj  ]
_ID = (count MyArray) - 1
PubArrayIndex = count MyArray ;publicvariable "PubArrayIndex"
Call format [ "PubArray%1 = _objDrone  ; publicvariable {PubArray%1}",_ID ]

And use it in a client or server

MyArray = []
_i  = 0
#BuildArray
_Temp = Call format [ "PubArray%1",_i   ]
MyArray = MyArray  + [_Temp ]
If (_i  < PubArrayIndex - 1 ) then {_i = _i +1 ; goto "buildArray"}

This sample is for init, if you want a real time update array you add a public variable update and you manage the array in loops for looking changes in the array, if yes set the update boolean to true and start a build array sequence.

With this 2 tips you can know how many of specific addons are on the map, and manage them.

- You can use event for starting a script, some events are executed on all computers (obj setdammage 0.001 start the script)

Hope this help someone  :)

Title: Re:Some tips for addon scripter & mission designer
Post by: Tactician on 15 Apr 2003, 02:24:10
Are you sure an init eventHandler only activates on the server?  Or just where the unit is local?
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 15 Apr 2003, 13:57:27
I'm sure after a hosting test, cause I use this method for a UAV script, for the moment it is in beta testing, and after this I will be compltly sure.

I have a loop server side with player globalchat in it, starts by client side you see only on server side the message, so the loop can't be local side.
Title: Re:Some tips for addon scripter & mission designer
Post by: Tactician on 15 Apr 2003, 21:27:40
Vehicles are always server-side unless a player is driving.  Have you tested an init eventHandler on a soldier that was being played by a remote human?
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 15 Apr 2003, 21:37:06
Oh you make me think to the cross init, for what I say it's for empty addons (uav it's necessary)
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 16 Apr 2003, 01:14:08
This method works, and works well. (This is how we kept track of CoC_Mines, with, well sure, certain minor differences).  But init EHs only fire locally.
If you have a unit in the group of a remote player, that init EH will not fire on the server.  You _might_ get some interesting synch problems, but that depends on how OFP works in MP (read: test that bridge when you get to it).

What I'm interested in uiox are the possibilities for loading dialogs into addon.  I am in fact working on a system that uses this MP registering trick and a few others for a highly flexible on-map artillery system.  Have you gotten around the description.ext limitation ?  And in any case, how "generic" can a predefined dialogue be? My current hint/action system allows each addon to specify its own display and menu structure.  In theory, this system would allow MP control over practically anything.
Is it possible to specify a "tabula rasa" dialog, and then "in mission" create one according to needs?

Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 16 Apr 2003, 13:03:29
Some other tips around MP for addon:
The name:
The object variable on server side 1675ce00# NOID CLASSOFADDON
The object variable on local side 1675ce00# NOID CLASSOFADDON REMOTE

I have done some test with call fonction but not possible, some test with string (call format ["%1",call format["%1",Object + " REMOTE"]] ) not possible all of this == bugs.

With this you can understand how OFP manage objects for a MP session.
On local side names of objects are differents, so if you apply to it a command in local the object has remote in end, BUT if you have store the names without remote (server side names), if you put it in line of code maybe (conditionnal cause I don't explore this way) you will have something like a public command.
In fact there is TWO inits one without remote, one with remote, don't test yet this way, have to confirm, not need, it's the problem of "start and get back" or desynchro, you give an order to the remote object, and immediatly after server set the object to the value of the server database.

For dialogs I have to do some test cause I have some strange effects with addons with same name of dialog and class name. Some other with files chain with #include, cause next months a lot of addons with dialogs will out.
This creates bugs, but bugs are sometime the path to a tip, and after debugging & release of the addon, it's the time of discover tests...
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 16 Apr 2003, 17:55:56

alors:
Quote
The name:
The object variable on server side 1675ce00# NOID CLASSOFADDON
The object variable on local side 1675ce00# NOID CLASSOFADDON REMOTE

I have done some test with call fonction but not possible, some test with string (call format ["%1",call format["%1",Object + " REMOTE"] ) not possible all of this == bugs.
Are you sure that's what the object is called remotely?  could it be: [1675ce00# NOID CLASSOFADDON, REMOTE] ?
I vaguely remember remotes being some sort of array.

Quote
With this you can understand how OFP manage objects for a MP session.
On local side names of objects are differents, so if you apply to it a command in local the object has remote in end, BUT if you have store the names without remote (server side names), if you put it in line of code maybe (conditionnal cause I don't explore this way) you will have something like a public command.
In fact there is TWO inits one without remote, one with remote, don't test yet this way, have to confirm, not need, it's the problem of "start and get back" or desynchro, you give an order to the remote object, and immediatly after server set the object to the value of the server database.


If I understand what you're saying here:

The problem is that most commands issued to a remote object are ignored.  Some are transmitted, but not all of them.

For each object in MP, there are two "names".  There is the "real" name, on the client (1675ce00# NOID CLASSOFADDON), and the "remote" name, on all the others (1675ce00# NOID CLASSOFADDON REMOTE).

Your hypothesis is that if a client can address an object by its "real" name, even if the object is not on that client, it will be able to affect it as if it were local.

Here are what I see to be the problems:

I. Names like 1675ce00# NOID CLASSOFADDON cannot be accessed from OFP scripting.  They're purely internal (like: WEST Alpha Black: 1).

II. "Remote" may serve to discard some signals, but it's probably also a means by which OFP knows to transmit state information to the local machine.  If we take away "remote", wouldn't we in effect be treating the object as if it were local on our machine, without affecting the machine that has the object as local?

or have I misunderstood you?

Quote
For dialogs I have to do some test cause I have some strange effects with addons with same name of dialog and class name. Some other with files chain with #include, cause next months a lot of addons with dialogs will out.
This creates bugs, but bugs are sometime the path to a tip, and after debugging & release of the addon, it's the time of discover tests...
addons with same name of dialog and class name? You mean, as if the config.cpp class were interfering with the function of the dialogue?  Would this suggest that OFP was looking in the config.cpp for the dialog information (and if this is indeed the case, don't keep the infos to yourself ;) )

so the description.ext
#Include "mysetofdialogues.sqh"
presents some problems?


well, we hope to get a beta version of our stuff out soon, sans dialog (action+hint), and maybe that will help spur further discussion.
 
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 16 Apr 2003, 19:57:26
Sorry something is wrong. The "names" are completly different, I have done a test with setpos on local side, this works:

Name without remote setpos [0,0,0]
Name with remote setpos [0,0,0]

with 2 difeerent addons, they go to the position 0.

Now I have to test this with an AI, with some commands only start on server side but call on local side, if this works you need only server side "name" of objects for doing moveto,etc...

For dialogue they are loaded when OFP start for addons. The strange bugs I didn't note them and I don't remember exactly what it is it. So I have to test.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 17 Apr 2003, 00:44:48
Hi Dinger, could you explain what "remote units" are?

As far as the dialogs, I think the only way is to have the player copy&paste something like

#include <addonname\bla.h>

to his description.ext. I'm also working on a mp-compatible dialog script, and so far it works fine with this method.
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 17 Apr 2003, 05:03:31
"remote unit" = a unit that is local to an instance of OFP other than the one currently running.

My dialog issue is the following:
I'm working on  a unified indirect fire package, where every indirect fire asset (=mod or in the unified mod) can specify its interface.  For hint screens and addaction menus, this is easy to do.  The question is: can you have a generic mod reference, possibly one included in a description.ext, that allows you to create any dialog you want from an array?
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 17 Apr 2003, 10:45:28
not sure I understand correctly, but generic dialogs are not problem I think. Make one with all graphics components you could possibly need, and switch them on/off using ctrlShow; the text can be added on the fly using ctrlSetText.
that way all mods could use the same generic dialog, and configure it as they need it. Even the color and background pics can be added on the fly, so no need to have each mod keep its own dialog definition; just do this via scripts.

Another q: As long as I launch my scripts from EH and add-actions (that are defined in config.cpp) I'm relatively save as far as synchronisation, right? Both EH and addactions are automatically run on all machines?

Quote

"If you have a unit in the group of a remote player, that init EH will not fire on the server"

So that init EH will run only on the machine where the unit is local right?




Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 17 Apr 2003, 12:54:11
Sorry something is wrong. The "names" are completly different

False they are identical, and I'm wrong with the init sequence there is two inits one local, and a server side.

For dialogs I don't understand your problem. If you do a real arty script, you need a chief of battery, and some pieces.
For an arty armored it's easy, you have only one piece, so the values in dialog are easy to broadcast.
But for many pieces I don't have the tip for the moment for knowing how many pieces the battery has.

The solution is in addonner side, an addonner can solve the problem, the battery is an addon compose of 5 pieces, and you know when init start the id , pos and dir of each pieces.
For manage this only a chief of battery and only one dialog.
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 17 Apr 2003, 19:36:30
RubbleMaker: quite the opposite.  Init EHs and Addactions are both local-only.  THey don't work across MP.
Quote
For dialogs I don't understand your problem. If you do a real arty script, you need a chief of battery, and some pieces.
For an arty armored it's easy, you have only one piece, so the values in dialog are easy to broadcast.
But for many pieces I don't have the tip for the moment for knowing how many pieces the battery has.
The way I'm doing it this isn't a problem.  With the howitzers I'm doing away with the "chief of battery" (FDC unit), and only doing the guns.  I have a double registration system: individual units register (CoCIFunitN), and "assets" register (CoCIFAssetN).  Each unit has an init EH that runs a "handler" script.  For some things (like the MLRS, a cruise missile launcher), a unit will be an "asset"; for others (like the m109), all the units in a group will be an "Asset".  So if you put four M109s in the same group, four init EHs fire, starting four scripts.  These scripts register each 109 as a "unit", look around, and determine how many howitzers are in the group.  One script registers the group of four m109s as an "Asset".

The master program on all clients loads in the units and the assets.  For each new asset type, it loads in an interface specific to that asset.  So broadcasting "dialog" type isn't necessary.

In this way, a single control script could be used in MP to acquire and control everything from mortars to cruise missiles to uiox's UAV.  But I don't know much about dialogs.
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 18 Apr 2003, 13:20:23
If I undstand you need a dialog with an update of informations in it, one part for collect orders, an other one for give orders.
If it's this, I have done.
Sorry, if I don't understand yet, it's always the same problem: language.
Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 18 Apr 2003, 13:36:57
Init EHs are local-only?

From the tests I did it seems like the script called by the init EH of a vehicle is run on the server no matter if the unit is in the group of a player (or player is pilot/driver) or not. (1.91 dedicated Win server, 1.90 clients)

For the cargo system I'm doing I have a similar situation with dialogs like you Dinger.
I'm using a string lookup table on the server and the clients for strings needed in dialogs to display the registered addons.
To setup the lookup table on the server and the clients I'm using a combination of init and fuel EH to register the addons. The init EH script is run on the server and sets the fuel to 0 and back again. This triggers the fuel EH on all the clients and starts the client side scripts.
This way I only need the #include line in the desfription.ext for the dialog but no init.sqs to start the server and client scripts.

The only problem is that if a vehicle is local on a client (in player group) the setfuel command called on the server doesn't work, so I have to createVehicle a special dummy vehicle that uses this init and fuel EH combination to get scripts running on the server and the clients for registering the addon.
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 18 Apr 2003, 15:13:07
What is EH?  ???
Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 18 Apr 2003, 15:25:50
EH = eventhandler

In this case the fuel and init eventhandler line in the config.cpp of an addon:

Code: [Select]
class eventhandlers
{
init  = "[_this select 0, 24] exec ""\cargo\scripts\unitinit.sqs""";
Fuel  = "[_this select 0, 24] exec ""\cargo\scripts\FuelEvent.sqs""";
};
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 18 Apr 2003, 16:46:21
Thx

The init eventhandler is both, local and server, for an addon you have an init for the server object and an init for the remote object.


No it's not right the init of addon is only server side I'm completly sure now.
Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 18 Apr 2003, 17:31:59
Are you sure about the init eventhandler also running on the clients?

In my example above: When I put a hint command in both the unitinit.sqs and the FuelEvent.sqs, then the hint of the unitinit.sqs is never shown on a client (no matter if the unit is local or not) but the FuelEvent hint is shown on every client.
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 18 Apr 2003, 18:38:28
It's for addon, the event init of a addon is start on the server and on each client.

And for script I have to test, for learn how apply what I have learned with addon scripting.
Note : I don't have a trigger server and I start a server loop and I can call it.

So I say to myself maybe it's possible in mission MP scripting.

****Edit

Only server side
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 18 Apr 2003, 19:53:19
Init EHs fire only on the machine local to the unit.  This can be a server or a client.
CoC_Mines relies on the "local-only" init.  That case with manned vehicles is interesting.

The fuel is a good trick rom, but you don't have to do it for each unit.  All you need is one dummy object to start a master control script.  CreateUnit and setfuel is good and automatic; I might "borrow" that idea.
What we're doing is a little more ostentatious: we're making a big fat obelisk (that emits a slight humming sound) that the mission designer has to put on the map for things to work.  It's init EH will indicate the server and launch server-side stuff.  It will also "fire" something (maybe a starter pistol sound, maybe just a green mist), and that will trigger the fired EH on all clients, loading the client-side stuff.
Client-side init script registers each asset, and indexes "TypeOf" against a list of known addons.  If it's not in the list, it loads the interface library (and any client-side scripts the asset requires)

Anyway, I hope to have a beta to show you soon (jostapo's been busy with work lately).
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 21 Apr 2003, 19:36:07
Alright, small reference so far.

Right now I'm missing information on wether the following commands are local or global:

VehicleRadio
cutRsc
animate


REFERENCE
========


GLOBAL
=======
scripts:

init.sqs
OnPlayerKilled.sys


commands:

setdir
setVelocity
setpos
RemoveWeapon
RemoveMagazine
SetFuel
DeleteVehicle

event handlers:

Fired, IncomingMissile, Damaged, Getin, Getout, Engine


LOCAL
======

commands:

drop
showCompass/Map/Radio&GPS...
say
inflame

event handlers:

Killed
Hit
init   (only where unit is local)



@Dinger: The addactions are local when added by script, but the actions defined in the addon's config.cpp are global I think (I mean the scripts triggered by them are run on all machines, no?)

Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 22 Apr 2003, 09:24:42
Useractions are local everywhere (addon and script added). In addon useractions one advantage you can modify radius detection, but in the script called you don't have the array pass by script action.

Some précisions

Event start on all clients are not really global. A script called by an event only start on all clients, exemple an animation (always local), If you do some commands broadcast by server you will have problem of repetition of the command (one by server one local),so you need trapped the server for avoid this.

A command is not really global when it needs the server side.
We can make a new classification

Global command : start everywhere, do everywhere (server + client)
Server side command : start on server broadcast on all clients
Local: start local
Title: Re:Some tips for addon scripter & mission designer
Post by: Tactician on 22 Apr 2003, 09:47:41
Some things:

- onPlayerKilled.sqs is triggered locally (only by the player).  It exists globally as part of the mission for all to see but is only called into action when the player dies.

- I would use the term "public" rather than "global".  Public events are events transmitted to all other clients, whereas the global scope can still be local to one client.  For instance, any variable can vary between clients until publicVariable is called to sync them.

- Actions added by script are not public.  If you use 'player' in a condition for a trigger adding an action (i.e. player distance object < 10) then only a player meeting those conditions will see the action.  I assume config.cpp actions are viewable by all players.

- No such thing as a server-side command; commands are either public or local, and a public event can be called from any client.  "Server-side" is only a method of implementing public commands, consolidating them to one reliable client (or dedicated server).  Yes, even createVehicle avoids duplication when only run by one player.  The server is just a logical traditional place to call it from, and there are reasons to call it from other clients.  The duplication property of createVehicle is a property of OFP vehicles, not OFP scripting.

- A neat way to use init eventHandlers.  If the eventHandler calls a script with _this as a parameter, the unit is the first parameter.  If you check ?(player != _this select 0): exit, any commands run afterwards will be local to the player if he is that type of unit, and AI troops will be excluded in the host's case.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 23 Apr 2003, 00:07:48
Well ok, so useractions are local no matter if installed by config.cpp or a script, but how to replicate local commands on all clients?

One idea that might help is to have a special script (I call it "replicator script") running for each useraction, which must be executed by the init.sqs so it runs on all machines. This script only waits until another script signals a useraction, and then it runs the local commands on all machines.

I thoght this through carefully and the problem is that scripting will turn into a mess this way. The replicator script would have to check on which machine it is running, and leave out the unit which called the useraction, or else the script commands will be dublicated.

I ended up thinking that the best way to take care of all this stuff is the way they did it in Quake: generic server-client buffers. That way *all* local commands will be run through a buffer that distributes the command that should be run to all clients. On each client, there is a script that waits for a new command in the buffer, and then dispatches that command. This buffer will be used even in SP to guarantee some continuity and to avoid the mess I that the "replicator" method would introduce. Running all local commands through such a buffer could actually make it possible to write scripts that work exactly the same in SP and MP, so no need for extra code anymore :)

What I'd like to know is if the overhead introduced by using such a buffer would be noticable. The buffer would have to be constructed with metavariables only,since arrays don't work with the "publicVariable" command.

Title: Re:Some tips for addon scripter & mission designer
Post by: Tactician on 23 Apr 2003, 13:27:08
I think the usage of an action is public if all clients see the action.  So, if you add an action to an object through its init field where all clients will see it, the script run by that action will be run by all clients.  To make a local action do something public, it's just a few publicVars:

init.sqs snippet:
Code: [Select]
publicObj = objNull
publicUnit = objNull
[] exec "checkaction.sqs"

Trigger activated repeatedly
Condition: alive player AND player distance (nearestObject [player,"SoldierE"]) < 10
Activation: reportAction = player addAction ["Report contact","reportaction.sqs"]
Deactivation: player removeAction reportAction

reportaction.sqs:
Code: [Select]
_obj = _this select 0
_unit = _this select 1
_index = _this select 2

;; broadcast the player and the enemy soldier he reported
publicUnit = _unit; publicVariable "publicUnit"
publicObj = _obj; publicVariable "publicObj"

;; remove action from player
_unit removeAction _index

checkaction.sqs:
Code: [Select]
#start
;; wait til object is broadcast by a player
@!isNull publicObj
;; store variable values
_obj = publicObj
_unit = publicUnit
;; reset the variables to null
publicObj = objNull
publicUnit = objNull
;; sideChat seen by _unit's side
_unit sideChat format["I found an enemy soldier!  His name is %1",name _obj]
goto "start"
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 23 Apr 2003, 15:46:09
RubbleMaker: bn880 is working on just such a suite of MP scripts, or at least he was before the CoC forums crashed; they were pretty far along too.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 23 Apr 2003, 23:40:37
Dinger: any chance I could get in contact with bn880? never saw him around here.

Well I just did a lil test with user-actions (defined in config.cpp), and it seems like NONE of these actions is available to anybody but the server. I was client and they were available, but nothing happened when I activated them.

So the question is: how can I make these actions become available to everybody? Do I have to add them on every client?

Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 24 Apr 2003, 00:03:58
One more q: when I have like 3 vehicles on a map, and all players start outside these vehicles, will the vehicles then be local to the server only? Coz that could be the reason why I couldn't use the actions: because I was client and the vehicles were all local to the server. However, then the script associated with these actions still should have executed on the server (although it was called from a client), which however it *did not*. The funny thing is that the useraction didn't do anything, neither on the server nor on the client.

any ideas?
Title: Re:Some tips for addon scripter & mission designer
Post by: Killswitch on 24 Apr 2003, 18:58:19
>One more q: when I have like 3 vehicles on a map, and all players start outside these vehicles, will the vehicles then be local to the server only?

Yes. I did some testing on the locality of units and vehicles a while ago. I had me (player),
a Jeep and 2 AI:s not in my group in the mission.

From start, the 2 AI:s are local to the (ded) server and so is the empty Jeep.

IIRC, when I got in the Jeep as driver, it then got local to my machine. It remained local to
my machine even after I got out. At least for a few seconds (didn't wait too long, so I don't know if the "locality" is transferred back to the server after a timeout)

I had a radio trigger that made one of the AIs get in as driver in the jeep, and if I then
entered in the back seat of the jeep, it was still remote (i.e. local to the server). I cant recall
what happened when I (via a trigger) had the AI get out... have to find my test mission again.

I also remember getting in the Jeep as cargo (empty driver seat) but cant recall if the Jeep
remained local to the server or was made local to my machine...

I belive the rule for vehicles is that it is (gets) local to the driver's/pilot's machine. Not sure who
gets locality in a tank (cmdr/driver/gunner), though.

Title: Re:Some tips for addon scripter & mission designer
Post by: Tactician on 24 Apr 2003, 19:32:21
Tanks seem to always be local to the driver.  This is why the tank driver should always be someone with a good connection :)

I should say that the tank hull is local to the driver.  The gun turret is local to the gunner and the commander turret is local to the commander, naturally.  Interestingly, I've never seen the tank turret skip around the way I've seen the tank hull skip around, probably due to its simplicity; all it has is direction, bank, and pitch or the barrel and turret (most of which can be gleaned from the position of the tank itself) and no messy velocity vectors to be broadcast.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 24 Apr 2003, 21:26:05
Small addition to the command reference; did a 'lil test yesterday:

VehicleRadio
cutRsc

are both local.

animate

is global. Might give synch probs if run from more than one machine, so I guess its best run from the server only.

btw. anybody experienced problems with using the "Time" function in MP?
I cannot confirm this yet, but I experienced some odd problems that might have to do with the Time function. I used it as the condition for scripts triggered by user actions, and none of the actions worked on the clients, only on the server they did. I might be wrong here, but I didn't find any other reason why the actions wouldn't work.
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 24 Apr 2003, 21:38:50
Don't use time for anything critical.
time resets on savegames, which is a problem in SP.
In MP, I'm not sure how well it's synchronized.  I guess that's easy to test.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 25 Apr 2003, 09:11:44
Allright, I settled all my useraction probs now; it was some wrong conditions in the scripts, and not a general problem :)

The challenge now is to properly replicate all local events on all clients. Therefore I will definetely use the client-server buffer communication I described before. It will allow my to use the same code for SP and MP, as even in SP there will be such a buffer (might introduce a very small latency, but the simplicitiy of it more than makes up for a lil speed penalty). Using the buffer method I no longer have to care about which events/actions trigger which scripts under which condition (i.e. if unit local to the machine). The problem is how to determine when an entry in the buffer (i.e. a command to be called on each client) can be removed. I first thought that having a simple counter would be sufficient, as each client would just increase that counter after dispatching the command, and kick it out once the counter equals the total number of clients. The problem is that the same client could dispatch the command twice this way. So I rather need to tag the command with something that says wether or not a specific client has already dispatched the command. This of course is where you usually use a bitmask, but ofp doesn't support it.
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 25 Apr 2003, 11:19:15
I have done something, work and test.
It's a MP dialog for a multi-squad script.

First create array of players and call inits (in init after server side test):

Code: [Select]
[ArrayGroupEastServer ] exec "Init\InitTaskServer.sqs"
Install server loops (select 0 is the group name, select 6 a predifened ID number) :

Code: [Select]
_ArrayOfparam = _this select 0

_i = 0
#Install
_SubArray = _ArrayOfparam select _i
[ _SubArray select 0, _SubArray select 6] exec "Task\ServerTask.sqs"
_i = _i +1
? _i < count _arrayOfParam : goto "Install"


the server side loop install for each player.

Code: [Select]
_Group = _This select 0
_IdGroup = _This select 1

;                  Assemblage de la variable d'attente

Call format ["PubWaitEvent%1%2 = false ; publicvariable {PubWaitEvent%1%2}",side _Group  ,_IdGroup ]
#return

;                   Mise en attente du script d'un appel
@(Call format ["PubWaitEvent%1%2",side _Group  ,_IdGroup ])
;                   Un appel a été demandé par client
;                  On remet la variable d'attente à faux car on boucle dans ce script
Call format ["PubWaitEvent%1%2 = false ; publicvariable {PubWaitEvent%1%2}",side _Group  ,_IdGroup ]

;                  Convention du systéme : lorsque un client appelle il dit aussi ce qu'il veux faire
;                  Donc on récupère l'action demandée
_TypeAction = Call format ["PubTypeOfAction%1%2",side _Group  ,_IdGroup ]

;                  On se branche sur l'action demandée
goto format ["%1",_TypeAction ]
;                  Un petit test au cas ça merde, car si on est ici c que ça pue :)
Goto "end"

;                  Déplacement d'un groupe
#1
_Xmove = Call format ["PubXmove%1%2",side _Group  ,_IdGroup ]
_Ymove = Call format ["PubYmove%1%2",side _Group  ,_IdGroup ]
 _group move [ _Xmove,_Ymove]

Goto "End"
;                  Changer le mode de combat
#2
_Combat = Call format ["PubCombat%1%2",side _Group ,_IdGroup  ]
Leader(_group ) setcombatMode (ArrayCombatGroup select _Combat)
Goto "End"
;                  Changer le mode de formation
#3
_Formation = Call format ["Pubformation%1%2",side _Group ,_IdGroup ]
Leader(_group ) setformation  (ArrayFormationGroup select _Formation)
Goto "End"
;                  Changer la vitesse
#4
_Speed = Call format ["PubSpeed%1%2",side _Group ,_IdGroup ]
Leader(_group ) setspeedMode  (ArraySpeedGroup select _Speed )
Goto "End"
;                  Changer l'engagement au combat
#5
_Behaviour = Call format ["PubBehaviour%1%2",side _Group ,_IdGroup ]
Leader(_group ) setbehaviour  (ArrayBehaviourGroup select _Behaviour)
Goto "End"
;                  Changer la position debout ou allongé
#6
_Unitpos = Call format ["PubUnitpos%1%2",side _Group ,_IdGroup  ]
{_x setUnitPos  (ArrayUnitposGroup select _Unitpos) } foreach units _group
Goto "End"
;                  Changer la direction ou le groupe scrute
#7
_WatchDir = Call format ["PubWatchDir%1%2",side _Group ,_IdGroup  ]
_Group setFormDir _WatchDir
Goto "End"

#End
;                  Se remet en attente
Goto "Return"


Local side caller script


Code: [Select]
;["Move",_Group,_PosMove] exec "eventmanager\ExecOnServer.sqs"         1
;["SetCombat",_whocall ,lbCurSel 1501] exec "eventmanager\ExecOnServer.sqs"      2
;["SetFormation",_whocall ,lbCurSel 1502] exec "eventmanager\ExecOnServer.sqs"      3
;["SetSpeed",_whocall ,lbCurSel 1503] exec "eventmanager\ExecOnServer.sqs"      4
;["SetBehaviour",_whocall ,lbCurSel 1504] exec "eventmanager\ExecOnServer.sqs"      5
;["SetUnitPos",_whocall ,lbCurSel 1505] exec "eventmanager\ExecOnServer.sqs"      6
;["SetFormDir",_whocall ,_TempDir ] exec "eventmanager\ExecOnServer.sqs"      7

_CurrentGroup = _this select 1
_Group = ArrayGroupParameters select _CurrentGroup select 0
_IDGroup = ArrayGroupParameters select _CurrentGroup select 6


_Where = _this select 0

Goto _Where
Goto "End"

#Move
_PosMove = _this select 2

Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 1 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubXmove%1%2 = %3 ; publicvariable {PubXmove%1%2}",side _Group ,_IdGroup,_PosMove select 0  ]
Call format ["PubYmove%1%2 = %3 ; publicvariable {PubYmove%1%2}",side _Group ,_IdGroup,_PosMove select 1  ]
Goto "End"

#SetCombat
_TypeCombat = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 2 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubCombat%1%2 = %3 ; publicvariable {PubCombat%1%2}",side _Group ,_IdGroup,_TypeCombat  ]
Goto "End"

#SetFormation
_TypeFormation = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 3 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["Pubformation%1%2 = %3 ; publicvariable {Pubformation%1%2}",side _Group ,_IdGroup,_Typeformation  ]
Goto "End"

#SetSpeed
_TypeSpeed = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 4 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubSpeed%1%2 = %3 ; publicvariable {PubSpeed%1%2}",side _Group ,_IdGroup,_TypeSpeed  ]
Goto "End"

#SetBehaviour
_TypeBehaviour = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 5 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubBehaviour%1%2 = %3 ; publicvariable {PubBehaviour%1%2}",side _Group ,_IdGroup,_TypeBehaviour  ]
Goto "End"

#SetUnitPos
_TypeUnitPos = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 6 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubUnitpos%1%2 = %3 ; publicvariable {PubUnitpos%1%2}",side _Group ,_IdGroup,_TypeUnitpos  ]
Goto "End"

#SetFormDir
_WatchDir = _this select 2
Call format ["PubWaitEvent%1%2 = true ; publicvariable {PubWaitEvent%1%2}",side _Group ,_IdGroup ]
Call format ["PubTypeOfAction%1%2 = 7 ; publicvariable {PubTypeOfAction%1%2}",side _Group ,_IdGroup ]
Call format ["PubWatchDir%1%2 = %3 ; publicvariable {PubWatchDir%1%2}",side _Group ,_IdGroup,_WatchDir  ]
Goto "End"

#End


And how execute a command in the dialog (not entire script, only main loop and events) :

Code: [Select]
_Update = time +1
;****************************************************************************   Main events loop
         #MainLoop
         ~0.0001
         ? (_OldUnit != lbCurSel 1500)and ( lbCurSel 1500 !=-1) : Goto "1500"
         ? (_OldCombat  != lbCurSel 1501)and ( lbCurSel 1501 !=-1) : Goto "1501"
         ? ( _OldFormation != lbCurSel 1502)and ( lbCurSel 1502 !=-1) : Goto "1502"
         ? ( _OldSpeed != lbCurSel 1503)and ( lbCurSel 1503 !=-1) : Goto "1503"
         ? ( _OldBehaviour != lbCurSel 1504)and ( lbCurSel 1504 !=-1) : Goto "1504"
         ? ( _OldUnitPos != lbCurSel 1505)and ( lbCurSel 1505 !=-1) : Goto "1505"
         ? ( _OldAction != lbCurSel 1508)and ( lbCurSel 1508 !=-1) : Goto "1508"
         ? ( _OldInstantAction != lbCurSel 1510)and ( lbCurSel 1510 !=-1) : Goto "1510"
         

         ? Notaction : goto "Notaction"; Notaction = false
         ? ConfirmAction : goto "ConfirmAction";ConfirmAction  = false

         ? _update > time : goto "skipUpdateInfos"
         _Update = time +1

         goto "UpdateValuesOfgroup"
         #skipUpdateInfos
         
?  !(cancel) and !(validation)and (ctrlVisible 1500) : goto "Mainloop"

;µµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµµ  End of Main event loop

? cancel : goto "cancel"
? !(validation) : goto "cancel"



? validation : goto "store"


#Store

cancel = false
validation = false
closedialog 0

goto "endScript"

#Cancel
cancel = false
validation = false
closedialog 0

#endScript



exit


;******************************************************* Management of events


#1500
? _OneOrderInProgress : lbSetCurSel [1500, _OldUnit ] ; Goto "MainLoop"
_OldUnit =lbCurSel 1500
 _WhoCall = lbCurSel 1500; _Return = "Return1500" ; _OldUnit == lbCurSel 1500; [ lbCurSel 1500] exec "gfolder\Group.sqs";goto "UpdateValuesOfgroup"
#Return1500
Goto "MainLoop"

#1501
_OldCombat =lbCurSel 1501
_CurrentCombat = lbtext [1501,lbCurSel 1501]
["SetCombat",_whocall ,lbCurSel 1501] exec "eventmanager\ExecOnServer.sqs"

#Return1501
Goto "MainLoop"

#1502
_Oldformation =lbCurSel 1502
_Currentformation = lbtext [1502,lbCurSel 1502]
["SetFormation",_whocall ,lbCurSel 1502] exec "eventmanager\ExecOnServer.sqs"


#Return1502
Goto "MainLoop"

#1503
_Oldspeed =lbCurSel 1503
_Currentspeed = lbtext [1503,lbCurSel 1503]
["SetSpeed",_whocall ,lbCurSel 1503] exec "eventmanager\ExecOnServer.sqs"


With this method you create all server side commands.
Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 25 Apr 2003, 11:26:51
To start stuff on the server and the clients you can exploit the createUnit command.
The init property of this command is treated like the init line of a unit in the mission editor and run on the server and the clients. The only thing you have to take care of is that the variables you use in the init are known and have the same values on the server and every client (just publicVariable them).
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 25 Apr 2003, 11:36:47
 :D Good tip Rom, never think, thx !
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 25 Apr 2003, 15:04:09
Very good idea rom! Some questions:

- will the "createUnit" init string be run on both client and server even if a game logic is created? Coz these normally are simulated only on the client

- can I use global vars to pass the names of other units to the init field? For example, if I want a unit to play a sound on all clients, using your method I'd simply use "createUnit" to make a game logic, and put something like

"globalUnitNameVar say ""sound"""

into the init string. But would all clients recognize the unit stored in
globalUnitNameVar? The unit names are different depending on wether they're local or remote, so maybe the above wouldn't work?
Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 25 Apr 2003, 16:49:09
I just got the idea of using the createUnit command for this purpose yesterday and I was only playing around with normal soldiers since now so I need to do some further tests to know how exactly this could be used.



[edit:]

I did the following test:

This is the init eventhandler in the config.cpp of a truck:
Code: [Select]
class eventhandlers
{
   init  = "[_this] exec ""\truckca\scripts\unitinit.sqs""";
};


this is the unitinit.sqs:
Code: [Select]
_obj = (_this select 0) select 0
truck1 = _obj
publicVariable "truck1"

~5

_org = group _obj
[_obj] join grpNull
_new = group _obj
truck1 sidechat "init truck1"
"Logic" createUnit [GetPos _obj, _new,"[truck1] exec ""\truckca\scripts\test.sqs""",0.5,"PRIVATE"]
[_obj] join _org

~10
deletevehicle ((units _new) select 0)

and this is the test.sqs:
Code: [Select]
(_this select 0) sidechat "truck1"
hint "test"

The test mission was the following setup:
1 unnamed playable truck
1 unnamed playable soldier
both in different groups

When running the mp mission on a dedicated server and connecting with two clients (one playing the truck and the othe rplaying the soldier), both the sidechat and the hint from the test.sqs worked on both clients. The sidechat in the unitinit.sqs wasn't seen on the clients

So my conclusion of this test would be that it doesn't matter if you createUnit normal soldiers or gamelogics, the init of the command is allways executed on the server and all the clients.
Also it doesn't matter if the objects that are used in the init are local or not as long as they were syncronized with publicVariable before.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 25 Apr 2003, 18:53:17
If this really works it'd be incredibly useful. One problem I see is that in oder to run a certain command on all clients, you need to pass a lot of information, e.g. unit name, position, command ID, etc pp.

So all this would have to be broadcast using global variables. Well, this I don't like really because it introduces other synchronization problems. Imagine you don't run that "unitinit" script from the init EH but from, say, the Engine EH. Now it'd be launched with each engine on/off event on each of the vehicles which have the EH installed. So chances are high they will overwrite the global variables that we use to pass the information. For example, the first truck would launch the script with a global variable

truck1= myTruck

then another EH kicks in an overwrites the variable with another vehicle:

truck1= hisTruck

now the same script runs twice and thus duplicates the command!

Thats a very nasty side effect. What I suggest so solve it is passing an init string which is compiled at runtime, not static, using the infamous "call format" trick. For example:

_dummyVector=[_truck]
_initstring=
Format ["[_dummyVector select %1] exec ""\truckca\scripts\test.sqs"""",0]

No idea if this works though; the problem is to paste the unit name into the string, which is quite a PITA in ofp.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 25 Apr 2003, 19:01:08
If it does not work, there might be another possibility:

we don't use the same global variable all the time; instead we use a set of metavaiables. Say we use 10 metavaiabes with indices 0..9, then we compute the index we gotta use for our script call using

index=(index+1)%10

so we toggle the metavariables with each script call, thus minimizing the chance that two scripts overwrite their variables.

Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 25 Apr 2003, 20:40:40
You will allways have those syncronisation problems when you try to run scripts on all clients. The only thing that makes the createUnit command easier is the way you get the scripts started on all clients.

I guess that the broadcast to the clients is done with a queue by OFP, so that if you execute 2 publicVriable commands on the server after each other the order of the variable changes on the clients and the server is allways the same (the broadcasting doesn't swap the two publicVariables).
So when you allways do the publicVariable on the server then the only syncronisation problem would be if there were two scripts running on the server at the same time.

An easy way to prevent this is the use of semaphore variables.
A simple example for your engine EH:

The engine EH calls a engineEH.sqs:
Code: [Select]
class eventhandlers
{
   Engine  = "[_this] exec ""engineEH.sqs""";
};

 This engineEH.sqs then uses the CreateUnit init to start a globalEngine.sqs script:
Code: [Select]
?!local Server) : exit

;wait until another server engine script is finished then set the semaphore to let other server scripts wait.
@EngineSemaphore==false
EngineSemaphore = true

_obj = (_this select 0) select 0
engineobj= _obj
publicVariable "engineobj"

~5

_before = units tempgroup
"Logic" createUnit [[0,0,0], tempgroup,"[engineobj] exec ""globalEngine.sqs""",0.5,"PRIVATE"]
_after = units tempgroup
_logic = (_after - _before) select 0

; now the sequence of publicVariable and createUnit is in the broadcast queue and nothing gets between those two commands on the clients too. So we reset the semaphore to let other scripts run.
EngineSemaphore = false



~100
deletevehicle _logic
(tempgroup is a group that you need to createUnit something, look at my previous post for an example how to get such a group)

Because of the use of the semaphore it isn't possible that the engineobj is changed by another engineEH.sqs between the publicVariable and the createUnit command.
The globalEngine.sqs then does the work that you want to be done on the server and the clients.

Normally this method should work quite well since even if it seem that two scripts are running at the same time, the CPU only works with one and then switches to the other after some time and back so that it only seems that the two scripts are running at the same time.
There is a really small chance that this switching between two scripts that run at the same time happens exactly between those two commands:
@EngineSemaphore==false
EngineSemaphore = true
This chance is really small and I didn't have any problems so far. But if this appears to be an issue then there are some enhanced semaphore technics that use a wait for a random time to prevent those problems with simultanious starting scripts.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 25 Apr 2003, 21:02:59
Yes I agree, the chance is *really small*, but I actually experienced such a situation once. When testing the scripts for my mission "The Prey", I observed a weird error in my voice player script. It occured only 2 or 3 times although I tested the scripts for several month, and it turned out that the problem was several scripts running simultaneously:

_unit say (_voice select _index)
_index=_index+1                                    <==
?_index>MAXINDEX:_index=0

I marked the line which caused the error. I had several instances of this script running, and sometimes ofp would pass control over to one script just when another script's process was at the marked line. I'd then get an overflow error.

So, the chance might be little, but it is still there. Especially in MP there is a certain latency between the server issuing the command from the game logic's init line, and the client running that command. In the meantime the global variable could well be overwritten by another script.

Your semaphore method does indeed solve the problem, but it introduces further latency, as now the scripts have to wait until the other scripts are finished. Also, the semaphore needs to be broadcast to all clients before the script is clear to launch. This latency might cause problems, especially if you do things like install a "Fired" eventhandler to a cannon or rocket launcher.

Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 26 Apr 2003, 19:54:57
rom, I tested your code today and it crashed all the time. Obviously the problem is in this line:

_org = group _obj
[_obj] join grpNull                          <=====
_new = group _obj
truck1 sidechat "init truck1"
"Logic" createUnit [GetPos _obj, _new, ....]

Basically you're calling createUnit with an empty group, which at least on my machine causes a serious crash (re-boot).

Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 26 Apr 2003, 20:16:45
That's really strange that this is crashing your PC.
When I'm trying to createUnit something with an empty group then it just doesn't create the unit. Also joining a unit to grpNull doesn't give me problems either.
(Clients: Windows XP, OFP:Resistance v. 1.91; Server: Win2k Server, OFP dedicated v. 1.91. Also no problems in SP and player server)

From what I've know so far is that joining a Unit to grpNull puts the Unit in the next available (new) group. This is the only way of creating new groups with scripting that I know of.
You can't name the Gamelogic in its init field because "this" is different on clients and server and using it will give you errors. So you need this grouping trick to delete the Gamelogic again.

Perhaps your _obj wasn't in a group at the beginning?
Units created with createVehicle don't have a group for example. Take the player as _obj for example to make sure _obj has a valid group.
Title: Re:Some tips for addon scripter & mission designer
Post by: WizzyWig on 26 Apr 2003, 20:31:50
uiox please can you send out demo download so we can c persisley how to install this as it looks like some people are haveing trubal with it thanks

Code: [Select]
JUST FOR YOU UIOX
Code: [Select]
uiox peut s'il vous plaît vous envoyez le chargement de modèle de demonstration si nous pouvons persisley de c comment installer ceci comme il a l'air de quelques gens ont trubal remercie avec cela
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 26 Apr 2003, 21:48:36
Well I had like 4 crashes when playing with that code, after the fourth one my CMOS settings had gone lost, so I'm going to be a bit more careful with that createUnit thingy now ;)

I'm using Win98 and ofp1.91 in SP.

"From what I've know so far is that joining a Unit to grpNull puts the Unit in the next available (new) group. This is the only way of creating new groups with scripting that I know of"

Yes I've seen that; when I join the created unit into GrpNull, it becomes Bravo Black #1 or something, whereas before it was Alpha.

"Units created with createVehicle don't have a group for example"

Yes that was probably my fault, I used createVehicle to make one gamelogic which then I wanted to use to make further game logics. That was when the crashes happened. I also tried using the player to do that, but it destroyed his group, i.e. after I called createUnit to drop a new unit into his group and removed it again, my group members were no longer available.

I hope there's a better way to use your trick, because messing with the player's group is not a very nice way. Maybe just tell the addon user to make one game logic that I use as reference.

btw. when I createUnit a game logic, and then join it into the player's group and then join it with GrpNull, is it assigned its own group?

Title: Re:Some tips for addon scripter & mission designer
Post by: rom on 26 Apr 2003, 22:30:43
No need to play with grpNull when you createUnit the Gamelogic into the players group, just delete it from the group again:

Code: [Select]
_playerGroup = group player
_playerGroupArray = units _playerGroup
"Logic" createUnit [[0,0,0], _playerGroup,"[engineobj] exec ""globalEngine.sqs""",0.5,"PRIVATE"]
_newPlayerGroupArray = units (group player)
_Logic = (_newPlayerGroupArray - _playerGroupArray) select 0
deleteVehicle _logic

But for your question:
Yes, I think that joining the Gamelogic to grpNull (after you createUnit it into the player group) will put it into a new group.
But first you have to name the Gamelogic with the array method before you can do anything with it.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 27 Apr 2003, 00:03:27
thx I try that code ASAP!

what do you mean with "array method"?
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 27 Apr 2003, 13:56:23
rom, the code you posted works fine, but when I create more than 1 gamelogic, it crashes again! Here's the code I use:

_playerGroup = group player
_playerGroupArray = units _playerGroup
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
"Logic" createUnit [[0,0,0], _playerGroup,"",0.5,"PRIVATE"]
hint format ["%1",(group player)]
exit

This is not aceptable, because when you have many scripts running simultaneously, it can happen that the player group receives more than one gamelogic at a time (because just before one script removes the logic, another script creates another logic).
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 28 Apr 2003, 03:33:01
a beta of bn880's MP services is due out _very_ soon.
I'll also put out a little function I wrote (really, it's graffiti compared to his calligraphy) called "PublicArray".  So check the chainofcommand in the next couple of days.
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 28 Apr 2003, 21:31:47
Array, we want in opfec to see the tip.

We are in OFPEC, you know?

Editing Center....

Advertisement Center

It's OFPAC....
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 29 Apr 2003, 06:27:07
I can't publish bn880's stuff uiox, and i haven't updated my stuff to make sure it still works.  Here's my scribblings, for what it's worth.  In a few days, the code that makes it work will be available.


init.sqs lines
Code: [Select]
@rNClientsReady
[] exec "publicArray\PublicArrayInit.sqs"

PublicArrayInit.sqs
Code: [Select]
;dinger@raf303.org
_publicarrays = ["Test1", "Test2", "Test3"]

?rNisServer == rNisServer:goto "NetworkInitRunning"
;insert variable initialized function
call preprocessFile "string\string.sqf"
call preprocessFile "numSystem\numSys.sqf"
call preProcessFile "Network\COMM_HEADER.sqf";

_connectTimeout = 10;
rNClientsReady = false;
_connectTimeout call loadFile "Network\initializeNetwork.sqf";

#NetworkInitRunning
_timeout = _time + 60

@(rNClientsReady || _time > _timeout)
?_Time > _Timeout: player sidechat "CoC_PublicArrayInit Error: failed to initialize network"; exit
if !ecool function ehre then {rPublicArrays = rPublicArrays + _publicarrays} else {rPublicArrays = +_publicarrays}





rPublicArrays = ["Test1", "Test2", "test3"]
;rPublicArrays = the names of the arrays to share

rNPA_PollInterval = .1
;set higher interval for lower urgency/traffic and higher overall performance, lower interval for high traffic/urgency

rNPA_Terminate = False
;set to TRUE to shut down polling

;rNPA_cc# = server/client channel (0 = master update) = time hack
;rNPA_ci# = server/client index = index to rPublicArrays
;rNPA_ind# = index reference from rPublicArrays -> nPAs
;rNPA_tim# = time hack for rPublicArrays updates
;rNPA_cp# = port opened to client
;rNPAA-rNPA9 = ports

;checkandload libraries
fPublicArray = preprocessfile "publicArray\fPublicArray.sqf"
?LIBNUMSYS > 0:goto "LoadPALib"
call loadfile "numSys\numSys.sqf"
#LoadPALIb
?LIBARRAYCNV > 0:goto "Initialize"
call preprocessfile "arrayConversions\arrayConversions.sqf"

#Initialize
rPublicArrayCount = count rPublicArrays
rPublicArrayPorts = []
_rPortStackDepth = 36
;nb This system is good only for 36 channels
;if you want more, we can add 36 to index, and go double-digit
rPublicArrayStem = "rNPa"
_i = 0

#BuildStack
_currentPort = rPublicArrayStem + (_i call fDecToAlpha)
call format ["%1 = time", _currentPort]
rPublicArrayPorts = rPublicArrayPorts + [_currentPort]
_i = _i + 1
?_i < _rPortStackDepth: goto "BuildStack"

#BuildIndices
_i = 0
{call format ["rNPA_ind%1 = -1; rNPA_tim = -1", (_i call fDecToAlpha)]; _i = _i + 1} ForEach rPublicArrays

;debug
player sidechat format ["%1", rPublicArrayPorts]

;start server
?local server:[] exec "publicArray\Server.sqs"

;start client
?player == player:[] exec "publicArray\Client.sqs"
exit


PublicArray.sqs
Code: [Select]
;dinger@raf303.org

_channel = rNClientChannel call fDecToAlpha
_push = format ["rNPA_cc%1", _channel]
_pushi = format ["rNPA_ci%1", _channel]
_tune = format ["rNPA_cp%1", _channel]

#Top
?count rPublicArrayOut == 0:exit
_arrayIndex = rPublicArrayOut select 0
call format ["%1 = time; %2 = _arrayIndex", _push, _pushi]
call {PublicVariable _pushi; PublicVariable _push}
player sidechat "Pushi = "+_pushi + "  _push = " + _push
_timeout = time + 10

#WaitForServer
?(call format ["%1 == (-1 * (_arrayIndex+5))", _pushi]):goto "FFE"
~rNPA_PollInterval
?time < _timeout: goto "WaitForServer"
player sidechat "PublicArray.sqs server timeout"
?!rNPA_Terminate:goto "Top"
exit

#FFE
;call format ["%1 = 0
_stemName = format ["rNPA%1", (call _tune) call fDecToAlpha]
_transmitArray = [call (rPublicArrays select _arrayIndex), _stemName] call fArrayToVar
{publicvariable _x} foreach _transmitArray
player sidechat format ["DEBUG - XMIT: %1: %2/Stem: %3, pushi: %4 = %5", (rPUblicArrays select _arrayIndex), _transmitArray, _stemname, _Pushi, call _pushi]

_timeout = time + 20
#WaitForAck
~rNPA_PollInterval
?time > _timeout:goto "TimeOut"
?(call format ["((%1 < -4) or (%1 > -1))", _pushi]):goto "WaitforAck"
?(call format ["%1 == -1", _pushi]):rPublicArrayOut set [0, -666]; rPublicArrayOut = rPublicArrayOut - [-666];goto "Top"

#TimeOut
player sidechat format ["PublicArray.sqs: NoAck (pushi: %1 = %2)", _pushi, call _pushi]
?!rNPA_Terminate:goto "Top"
exit

server.sqs
Code: [Select]
;dinger@raf303.org

;initialize clientchannels
_clients = (count rNClientList)
_i = 0
{call format ["rNPA_cc%1 = -1; rNPA_ci%1 = -1", (_i call fDecToAlpha)]; _i = _i + 1} ForEach rNClientList

rPublicArrayPortCurrent = 0
rPublicArrayPortsOpen = []

#RequestScan
_i = 0
#RequestScanLoop
_Channel = Format ["rNPA_cc%1", (_i call fDecToAlpha)]
? (call (_channel + " != -1")):player sidechat format ["Opening Channel %1", _channel]; call format [{%1 = -1; [_i] exec "PublicArray\ServerOpen.sqs"}, _channel]
_i = _i + 1
?_i < _clients: goto "RequestScanLoop"
~rNPA_PollInterval
?!rNPA_Terminate:goto "RequestScan"
exit

ServerOpen.sqs
Code: [Select]
;dinger@raf303.org
_channel = ((_this select 0) call fDecToAlpha)
_indexVar = format ["rNPA_ci%1", _channel]
_portVar = format ["rNPA_cp%1", _channel]
_index = call _indexVar
_ia = _index call fDecToAlpha
_port = 0
call format ["_port = %1; rPublicArrayPortsOpen = rPublicArrayPortsOpen + [%1]", rPublicArrayPortCurrent]
call {while {rPublicArrayPortCurrent in rPublicArrayPortsOpen} do {rPublicArrayPOrtCurrent = rPublicArrayPortCurrent + 1; if (rPublicArrayPortCurrent >= count rPublicArrayPorts) then {rPublicArrayPOrtCurrent = 0;}}}
_portIndexName = format [rPublicArrayStem + "%1_", _port call fDecToAlpha]
call (_portIndexNAme + " = nil")
call format ["%1 = _port", _portVar]
call (_indexVar + { = -1 * (_index+5)})
call {PublicVariable _portVar; PublicVariable _indexVar}
player sidechat format ["SO.sqs: Portvar: %1:%2, _port %3, IndxVar: %4,%5, _portIndexName %6", _portvar, call _portvar, _port, _indexvar, call _indexvar, _portIndexName]


_timeout = _time + 30
player sidechat "SO.sqs: Waiting for Xmission"
#WaitForTransmission
?(call _portIndexName) == (call _portIndexName):goto "TransmissionReceived"
?_time > _TimeOut:goto "TimeOut"
~rNPA_PollInterval
goto "WaitForTransmission"

#TransmissionReceived
player sidechat "SO.sqs: Xmission received"
call (_indexVar + { = -1})

_arrayIndex = format ["rNPA_ind%1", _ia]
_arrayTimeIndex = format ["rNPA_tim%1", _ia]
;player sidechat format ["aI = %1, ATI = %2, IV:%3 = %4, Ind = %5, IA = %6", _ArrayIndex, _ArrayTimeIndex, _IndexVAr, call _indexVar, _index, _ia]
_portAuthentication = _portIndexName + "_tim"
call (_portAuthentication + "=time")
call (_arrayIndex + " = _port")
call (_arrayTimeIndex + "= time")
rNPA_hack = time

call {publicvariable _arrayindex; publicvariable _arrayTimeIndex; publicVariable _portauthentication, publicvariable "RNPA_hack", publicvariable _indexVar}
goto "end"

#TimeOut
player sidechat "SO.sqs TimeOut"
call (_indexVar + { = -2})
publicvariable _indexVar
;wait for client to finish transmission or disconnect
~60

#end
rPublicArrayPortsOpen = rPublicArrayPortsOpen - [_port]
exit

Client.sqs
Code: [Select]
;dinger@raf303.org
_i = 0
fReceiveArray = preprocessfile "publicArray\fReceiveArray.sqf"
rPublicArrayOut = []
rPublicArrayIndex = []

{rPublicArrayIndex = rPublicArrayIndex + [-1]} ForEach rPublicArrays
{call format ["rNPA_ind%1", _i call fDecToAlpha]; _i = _i + 1} ForEach rPublicArrays
_hack = -1
rNPA_ccA = _hack
~(random 1)
player sidechat "ClientProcessRunning"

#MonitorLoop
?rNPA_hack != _hack: rNPA_hack = _hack; call fReceiveArray; player sidechat "hack"
~rNPA_PollInterval
?!rNPA_Terminate:goto "MonitorLoop"
exit


ArrayVar.sqf
Code: [Select]
/*
Array conversion sublibrary
Dinger March 6, 2003.
www.thechainofcommand.net
*/
If (local server) then {CoCArrayWidget = "Logic" createvehicle [-10000,-10000,0]; CoCArrayNullWidget = "Logic" createvehicle [-900,900,0]; PublicVariable "CoCArrayWidget"; PublicVariable "CoCArrayNullWidget"};
fVarToArray = preprocessfile {fVarToArray.sqf};
fArrayToVar = preprocessfile {fArrayToVar.sqf};
LIBARRAYVAR = 1

fPublicArray.sqf
Code: [Select]
private ["_array", "_arrayname", "_i"];
_arrayname = _this;
_i = 0;
if (!(_arrayname in rPublicArrays)) then {hintc format ["ERROR: ARRAY %1 NOT IN PUBLIC ARRAY LIST", _arrayname]}
   else
   {While {(rPublicArrays select _i) != _arrayname} do
   {_i = _i + 1}};
if (count rPublicArrayOut > 0) then
   {rPublicArrayOut = rPublicArrayOut + [_arrayname]}
   else
   {
   rPublicArrayOut = +[_i];
   [] exec "PublicArray\PublicArray.sqs";
   };
;


Code: [Select]
private ["_i", "_ind", "_ia", "_tim", "_channel", "_stemname"];
_i = 0;
while {_i < rPublicArrayCount} do
   {
   _ia = (_i call fDecToAlpha);
   _tim = format ["rNPA_tim%1", _ia];
      if (call format ["rNPA_tim%1 > (rPublicArrayIndex select _i)", _ia]) then
      {
      _ind = (call format ["rNPA_ind%1", _ia]) call fDecToAlpha;
      _stemname = format [{%1%2}, rPublicArrayStem, _ind];
      player sidechat format ["Receiving: %1 = %2", _stemname, call _stemname];
      call format [{%1 = +((_stemname) call fVarToArray)}, (rPublicArrays select _i)];
      rPublicArrayIndex set [_i, (call format ["rNPA_tim%1", _ia])];
      player sidechat format ["DEBUG - REC: %1 : %2", rPublicArrays select _i, call (rPublicArrays select _i)];
      };
   _i = _i + 1;
   };
;
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 29 Apr 2003, 06:31:55
Missed a library.
arrayConversions.sqf
Code: [Select]
/*
Array conversion sublibrary
Dinger March 6, 2003.
www.thechainofcommand.net
*/
If (local server) then {CoCArrayWidget = "Logic" createvehicle [-10000,-10000,0]; CoCArrayNullWidget = "Logic" createvehicle [-900,900,0]; PublicVariable "CoCArrayWidget"; PublicVariable "CoCArrayNullWidget"};
fVarToArray = preprocessfile {arrayConversions\fVarToArray.sqf};
fArrayToVar = preprocessfile {arrayConversions\fArrayToVar.sqf};
LIBARRAYCNV = 1;

ArrayToVar.sqf
Code: [Select]
/*
PURPOSE: Convert a multidimensional array of Booleans, Floats or Units into a sequentially numbered group of individual variables.
HISTORY:    CoC Dinger 03/06/2003
INPUTS:   _array: Multidimensional array of booleans, floats or units.
      _stemname: base name for metavariables (to be inflected with numerical series)
RETURNS:    one-dimensional array of strings, containing the names of content and index variables describing the array.
ALGORITHM:  
END ALGORITHM
CALL EXAMPLES:
 
   _array = [True, player, [32, leader group player], 5]
   _stemname = "rNVp1"
   [_array, _stemname] call fArrayToVar -> ["rNVp1A", "rNVp1B", "rNVp1C", "rNVp1C_", "rNVp1C_A", "rNVp1C_B", "rNVp1C_", "rNVp1D", "rNVp1_"]
*/
private ["_stemname", "_array", "_element","_output", "_i", "_elementname", "_indexname"];

_array = _this select 0;
_stemname = _this select 1;
_indexname = format ["%1_", _stemname];
_output = [];
_i = 0;

while {_i < count _array} do
    {
   _element = _array select _i;
   _elementname = _stemname + (_i call fDecToAlpha);
   if (_element in [_element]) then
      {
      call (_elementname + {= _element});
      _output = _output + [_elementname];
      }
   else
      {
      if (format ["%1", _element] == "scalar bool array string 0xfcfffef") then
         {
         call (_elementname + {= CoCArrayNullWidget});
         _output = _output + [_elementname]
         }
      else
         {
         call (_elementname + {= CoCArrayWidget;});
         _output = _output + [_elementname] + ([_element, (_elementname + "_")] call fArrayToVar);
         };
      };      
   _i = _i + 1;
   };

call (_indexname + "= _i");
_output = _output + [_indexname];
_output

VarToArray.sqf
Code: [Select]
/*
PURPOSE: Convert a sequentially numbered group of content and index variables into an array
HISTORY:    CoC Dinger 03/05/2003
INPUTS:   _stemname: base name for metavariables.
RETURNS:    multidimensional-dimensional array; destroys content and index variables on local machine
ALGORITHM:  Really, I stole this header from Bn880
END ALGORITHM
CALL EXAMPLES:
 
   _stemname = "rNVp1"
   _stemname call fVarToArray -> [True, Player, [32, leader group player], 5]


*/
 private ["_stemname", "_array", "_element","_output", "_i", "_elementname", "_indexname", "_index"];
_stemname = _this;
_indexname = _this + "_";
_index = call _indexname;
_output = [];
_i = 0;

while {_i < _index} do
   {
   _elementname = _stemname + (_i call fDecToAlpha);
   _element = call _elementname;

   if (_element in [CoCArrayWidget, CoCArrayNullWidget]) then
      {
      _output = _output + [((_elementname + "_") call fVarToArray)]}
   else
      {_output = _output + [call _elementname]};

   call (_elementname + "= nil");
   _i = _i + 1;
   };
call (_indexname + "= nil");
_output
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 29 Apr 2003, 11:29:16
When I say tip, it's not your code, it's nice to publish it, but the method you apply. MP scripters has different problem sometime and need adapt or build new code.
Title: Re:Some tips for addon scripter & mission designer
Post by: Teote on 02 May 2003, 03:43:59
Can someone please explain to me what ?(local server) does?

I've herd this before:

If you want your script to be run on a local computer then use:
?(local server):

or if the script is to be only run on the server then have the following line at the start of the script:

?!(local Server): exit

but doesnt ?!(local server): exit translate into, if not on local computer exit. If that is correct wouldnt ?(local server):  be the same as ?!(local server): exit when you are trying to run a command on a server? ???

I don't know if that made any sence to anyone but to me these 2 if statements counterdict eachother and would not let a script be run on a server.
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 02 May 2003, 10:03:08
uiox: yes, I understand that; but I tried my best to explain what I was doing in tips before.  But I'll wager a bottle of 10-year-old calvados there's nothing less ambiguous than a bunch of code.  Hmmm. forget the wager.  I'll buy regardless, and we can argue it out.  (I would have said 25-yr-old, but I'm a poor student)

Teote: the solution to your problems lies in this: server is not a reserved variable.  In a mission, you can put a line:
server = "MyLeftGnut"
and it won't give you any trouble.

But many MP scripts require that you place in your mission a gamelogic, and that you give it the name "server".
In MP, each computer keeps track of some part of the game.  Basically, each client keeps track of all the units in the client's group, and the server takes care of all the other units.  Thus, all gamelogics established at the beginning of a mission are taken care of on the server.
local (unit) returns a boolean on whether the unit is being taken care of by the particular computer.  so for units that are placed at the beginning of a mission and are not in a player's group, local (unit) will only return TRUE on the server.

Therefore, if you place a gamelogic called "server" in a mission, <local server> will only be true on the server.  That's how you figure out what the server is.
Title: Re:Some tips for addon scripter & mission designer
Post by: Terox on 02 May 2003, 23:33:06
forgive my ignorance, ive read through all the posts on this thread
I understand the difference between all the variable types, server side scripting etc

I dont understand what this thread is all about

Are you trying to get all clients to process the same data that the server is processing , with the end aim to reduce desync or something on those lines
I am lost a bit
Would prefer a brief explanation in laymans terms

Thanks ;D
Title: Re:Some tips for addon scripter & mission designer
Post by: Teote on 03 May 2003, 01:56:34
Dinger thank you that makes more sence and I think I understand it.

So does the server take care of all the collisions from all of the entities and does each computer take care of ever entity?
Title: Re:Some tips for addon scripter & mission designer
Post by: uiox on 04 May 2003, 21:36:32
If you kill a unit during a MP mission, you see the unit fall down. Your client sends a variable/event to the server, if the variable/event is lost during transfert, your unit will "respawn" when server will do an update.
Sometime when server lag the unit disepears and "respawn" 20 meters away, when the server update your data.
You can imagine what a server needs for preserve the integrity of MP mission. Imagine for understanding how it works because we don't have infos from BIS.

This thread is for tips:

Some commands are only server side. For start a command, a formation of your squad for exemple, you need to execute it on server side. For start a command on the server you need a loop only on it (if not the command will do 2 times, local and server, can create bugs or bad effects).
For detect the server you use a logic (Dinger have explain well that), and start the loop. After you call your loop with public variables, it's a part where the imagination of the scripter is necessary because you don't have array and string, so the most part of the time you have to imagine your own conventions.

In this thread you can see some commands test by scripters if they need to be server side or on each clients or only local.

An other good tip for MP scripting the Rom's tip:
How to start a script on each client with the init field of the command createvehicle. More he explains how to start a script DURING MP Mission a script only on the server.

And last Dinger's code for solve advanced MP scripting. In it you can see how build a public variable with call, many tests for see or wait for the status of server, how to test if transmission is receive, and many other tips...

I know it's a little bit in desorder, but if this thread can help some MP scripters, cause test MP mission is hard.
Last tip
For test if your PC is powerfull you can test a MP mission, you start a first OFP do the server and start a second session of OFP for the client on the same PC.
Title: Re:Some tips for addon scripter & mission designer
Post by: Rubble_Maker on 04 May 2003, 23:30:37
"For test if your PC is powerfull you can test a MP mission, you start a first OFP do the server and start a second session of OFP for the client on the same PC. "

Wow I didn't know thats possible - gr8 tip uiox!

btw. stupid question but I don't know: How do I find out the adress of the server when I want to join with the client instance of OFP?
Title: Re:Some tips for addon scripter & mission designer
Post by: Dinger on 04 May 2003, 23:45:38
use "LAN" instead of internet to connect to the machine
and yeah, MP testing is a PITA.
VB's editing console is really useful.
Title: Re:Some tips for addon scripter & mission designer
Post by: Terox on 05 May 2003, 12:05:56
right I think i got it!
, so this thread is basically about reducing packet loss and thus desync