PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Befehlsübertragung in Multiplay mit Auslöser


Vienna
21.05.2009, 16:01
Oft kommt es vor, dass im Editor positiv getestete Missionen im Multiplayermodus nicht korrekt laufen. Das liegt meist daran, weil bestimmte Befehle auf jedem Spieler-PC lokal ausgeführt werden müssen.

Für den Multiplayermodus gibt es ab ArmA V1.09 auch die Möglichkeit Befehle (Code) mit publicVariable zu übertragen. Das ermöglicht nun eine lokale Befehlsausführung an allen gewünschten PCs im Multiplayermodus über Auslöser.

Ein ebenfalls neuer publicEventHandler hat den Nachteil, dass Befehle nicht auf dem PC des Absenders ausgeführt werden. Mit den "Auslöser-Befehlsempfängern" wird der Befehl auch beim Absender ausgeführt (falls er für ihn vorgesehen ist).

Die Befehle brauchen einfach nur in die entsprechenden Variablen angeführt und mit publicVariabel übertragen werden.

Folgende Variable sind für die Übertragung vorhanden:

BefehlAnAlle (Befehle werden bei Server und Clients ausgeführt)
BefehlAnWest (Befehle werden nur bei West-Spielern ausgeführt)
BefehlAnEast (Befehle werden nur bei East-Spielern ausgeführt)
BefehlAnServer (Befehle werden nur beim Server/Host ausgeführt)
BefehlAnPlayer (Befehle werden nur bei den Spielern ausgeführt)

Beispiel mit dem ein Spieler ein Skript beim Server/Host startet:

BefehlAnServer = {[] exec "Skriptname.sqs"};
publicVariable "BefehlAnServer";

Beispiel um bei den Westspielern eine "hint"-Meldung anzuzeigen:

BefehlAnWest = {hint "Hallo Westspieler!"};
publicVariable "BefehlAnWest";

Es können auch mehrere -durch ; getrennte- Befehle angegeben werden. Eine Befehlsübertragung mit den Variablen ist mehrfach möglich. Zwischen den Übertragungen mit der selbigen Variablen sollte aber mindestens 1 Sekunde Abstand liegen.

Um die Befehls-Auslöser zu verwenden, brauchen nur die 5 Auslöser in die Mission kopiert werden (Kopieren: Strg+C und Einfügen: Strg+V). Es können auch nur die Auslöser kopiert werden, welche in der eigenen Mission Verwendung finden.

In der Beispielmission ist ein Test der Übertragung möglich. Es sind Funkauslöser vorhanden, welche bei Markern die Farbe ändern. Es wird nur der lokale Markerbefehl genommen, aber durch die Befehlsübertragung läuft die Umfärbung an allen zutreffenden PCs.

Xeno
22.05.2009, 08:47
Ein ebenfalls neuer publicEventHandler hat den Nachteil, dass Befehle nicht auf dem PC des Absenders ausgeführt werden. Mit den "Auslöser-Befehlsempfängern" wird der Befehl auch beim Absender ausgeführt (falls er für ihn vorgesehen ist).
Das Ausführen eines Befehls auf der Sender Seite bei Nutzung von publicVariable Eventhandler ist kein Problem.

Schick den Befehl an eine Funktion die je nach Parameter den Befehl auf der Senderseite ebenfalls noch ausführt bevor der Befehl über das Netz wandert.

Beispiel:

[{"West" setMarkerColorLocal "ColorRed"; player groupChat "Marker West auf rot!"}, 1] call SendToWest;

SendToWest = {
BefehlAnWest = _this select 0;
if ((_this select 1) == 1) then {call (_this select 0)};
publicVariable "BefehlAnWest";
};

"BefehlAnWest" addPublicVariableEventhandler {
call (_this select 1);
};
Dadurch brauchst Du z.B. nur die publicVariable Eventhandler auf den Clients zu installieren, die sie auch benötigen. Sprich, BefehlAnWest Handler nur auf den Clients, die auch auf der West Seite sind, usw.

Dein ganzes Beispiel lässt sich so komplett ohne einen einzigen Auslöser erstellen.

Xeno

Vienna
22.05.2009, 11:43
Natürlich geht das auch über den EventHandler und ohne Auslöser (dazu wurde dieser neue EventHandler ja hinzugefügt).

Ich habe es aber absichtlich mit Auslöser gemacht, weil es so einfacher und ohne ein Skript verwendbar ist.

In deinem Beispiel ist die Variable nach der Befehlsausführung noch zurückzustellen. Das selektierte Installieren der EventHandler verkompliziert die Sache nur. Auch ist es nicht ideal, dass bei jedem Aufruf erst festgelegt werden muss, ob der Absender auch den Befehl ausführen soll. Wenn ein Befehl für West ist, dann hat er automatisch für alle Westeinheiten zu gelten.

Über Skript würde ich das so auslegen, dass nur eine Variable und somit nur ein Eventhandler notwendig ist. Hauptsächlich für den Server. Bei den Clients sind Überschneidungen nicht ausgeschlossen, das wäre nur mit einer eigenen Variablen für jeden Spieler abwendbar.

Überhaupt sollte das Übertragen von Code nur sehr sparsam eingesetzt werden, weil die Fehler bei schlechter Internetverbindung noch umfangreicher werden. Ideal wäre die Übertragung von Zahlenkodes, welche dann bestimmte Befehle auslösen.

Xeno
22.05.2009, 12:00
In deinem Beispiel ist die Variable nach der Befehlsausführung noch zurückzustellen.

Falsch, eben das braucht man nicht.


Das selektierte Installieren der EventHandler verkompliziert die Sache nur. Auch ist es nicht ideal, dass bei jedem Aufruf erst festgelegt werden muss, ob der Absender auch den Befehl ausführen soll. Wenn ein Befehl für West ist, dann hat er automatisch für alle Westeinheiten zu gelten.

Auch das lässt sich nach Bedarf ändern, dass was ich geposted habe war nur ein Beispiel.


Über Skript würde ich das so auslegen, dass nur eine Variable und somit nur ein Eventhandler notwendig ist. Hauptsächlich für den Server. Bei den Clients sind Überschneidungen nicht ausgeschlossen, das wäre nur mit einer eigenen Variablen für jeden Spieler abwendbar.

Komm mal etwas aus dem alten publicVariable Denken raus.
Du kannst von 100 Rechnern aus eine pub Var an einen publicVariable Eventhandler schicken, alle werden ausgeführt, kein Zurücksetzen von Variablen notwendig.


Überhaupt sollte das Übertragen von Code nur sehr sparsam eingesetzt werden, weil die Fehler bei schlechter Internetverbindung noch umfangreicher werden.

Das ist ebenfalls falsch.
Ansonsten würde ein Mod wie z.B. A.C.E. nicht funtkionieren. Dort wird zig Code über ein und den selben Handler geschickt, es funktioniert trotzdem.


Ideal wäre die Übertragung von Zahlenkodes, welche dann bestimmte Befehle auslösen.
Auch das ist machbar, setze ich so in der Domina ein, allerdings schicke ich größtenteils Strings.

Xeno

Vienna
22.05.2009, 13:14
Verstehe ich das richtig, wenn ich mit publicVariable die Variable übers Netz sende, dann wird der EventHandler aktiviert, egal welchen Inhalt die Variable hat? Wenn das so ist, dann erübrigt sich natürlich das Rückstellen der Variablen (das wäre dann nur für die Bedingung bei Auslösern notwendig). Bis jetzt war ein Ereignis nur einleitbar, wenn sich der Inhalt der Variablen geändert hat. Ich habe kein Netzwerk, darum kann ich das nicht überprüfen.

Weiters, wenn zwei Spieler gleichzeitig mit publicVariable die gleiche Variable übers Netz senden, dann wird der EventHandler auf unbeteiligten PCs zweimal, bei den beteiligten nur der vom anderen Sender aktiviert (da könnte es Probleme bei der "Eigenaktivierung" geben)?

Ob das System einen oder hundert EventHandler überprüfen muss, wird sich wohl schon auf die Rechnerleistung auswirken. Ebenso sind Übertragungs- und Koordinierungsfehler bei größeren Datenmengen (z.B. Code) wahrscheinlicher. Hauptsächlich wenn sie von Clients erfolgen, die oft sehr unterschiedliche Übertragungsgeschwindigkeiten haben. Dazu habe ich aber keine Ahnung, wie das in ArmA koordiniert wird. Bei OFP war es noch schrecklich.

Jedenfalls danke ich dir für deine Ausführungen. Wenn ich vielleicht nochmal eine MP-Mission schreibe, werde ich sicher noch mehr Tipps von dir brauchen.

Xeno
23.05.2009, 01:27
Verstehe ich das richtig, wenn ich mit publicVariable die Variable übers Netz sende, dann wird der EventHandler aktiviert, egal welchen Inhalt die Variable hat? Wenn das so ist, dann erübrigt sich natürlich das Rückstellen der Variablen (das wäre dann nur für die Bedingung bei Auslösern notwendig). Bis jetzt war ein Ereignis nur einleitbar, wenn sich der Inhalt der Variablen geändert hat.

Korrekt. Der publicVariable Eventhandler reagiert sobald die Variable von irgendwoher kommt. Das ist der entscheidene Vorteil.


Ich habe kein Netzwerk, darum kann ich das nicht überprüfen.

Du brauchst kein Netzwerk dafür, starte einfach einen dedicated Server (arma_server.exe) auf Deinem Rechner und connecte auf dem.


Weiters, wenn zwei Spieler gleichzeitig mit publicVariable die gleiche Variable übers Netz senden, dann wird der EventHandler auf unbeteiligten PCs zweimal, bei den beteiligten nur der vom anderen Sender aktiviert (da könnte es Probleme bei der "Eigenaktivierung" geben)?

Nö, gibts keine Probleme.
Du darfst allerdings in dem addPublicVariableEventhandler nicht direkt die Variable abfragen, sondern _this select 1. _this select 1 beinhaltet den Variablenwert.


Ob das System einen oder hundert EventHandler überprüfen muss, wird sich wohl schon auf die Rechnerleistung auswirken. Ebenso sind Übertragungs- und Koordinierungsfehler bei größeren Datenmengen (z.B. Code) wahrscheinlicher. Hauptsächlich wenn sie von Clients erfolgen, die oft sehr unterschiedliche Übertragungsgeschwindigkeiten haben.

Nein, gibt keine nennenswerten Probleme in diesem Bereich, absolut nicht.


Jedenfalls danke ich dir für deine Ausführungen. Wenn ich vielleicht nochmal eine MP-Mission schreibe, werde ich sicher noch mehr Tipps von dir brauchen.
Leider, leider ist der MP Bereich bei ArmA einer der schwierigsten, gerade weil es so gut wie keine Informationen dazu gibt.
Das meiste lernt man nur durch try and error. Mit ein paar Einheiten platzieren im Editor ist es im MP Bereich nicht getan.
Die meisten die sich daran versucht haben, scheitern schon am Lokalitätsproblem geschweige denn haben je eine Chance solche netten Dinge wie die public Variable Eventhandler überhaupt zu verstehen.

Xeno

Vienna
23.05.2009, 10:58
Das ist eine gute Nachricht, dass man MP-Missionen, durch Starten eines dedicated Servers am eigenen PC, mit nur einem PC testen kann. So sind Fehler, welche durch Lokalitäten entstehen, einfach zu erkennen.

Vocoder
23.05.2009, 12:09
Ja der Tipp hatte mir auch ordentlich weiter geholfen, man kommt den Fehlern so einfach schnell auf die Schliche. ;)

Aber irgendwie komm ich nicht so recht an die SQF Syntax ran, da bedarfs mal einer Idiotensicheren Erklärung, pö a pö! :D

Vienna
23.05.2009, 13:39
SQF syntax - Bohemia Interactive Community (http://community.bistudio.com/wiki/SQF_syntax)

Wenn du dabei auch die weiterführenden Links durchliest, bist du eigentlich gut bedient.

UMPCHunter
31.05.2009, 11:02
Jedes Byte in einer Variable die per publicVariable übertragen wird braucht Traffic !

Und zwar für JEDEN! Client, sowie natürlich den Server.

Natürlich kann man nach dem Emfangen Abfragen machen, ob die Nachricht für sich selbst ist. Aber Traffic wird trotzdem verbraucht.

Das ist der Grund, warum man keine (langen) Strings senden sollte. Also auch keinen CODE !!

Xeno
31.05.2009, 19:43
Hunter...

Ich habe da andere Erfahrungen gemacht.

Ein paar Strings die gesendet werden machen den Bock nicht fett im Vergleich z.B. zu den dauernden setPos Übertragungen von allen Objekten die über das Netz laufen.

Senden von Code macht da gar nichts, auch nicht von längeren Passagen.

Xeno

Langer
30.05.2010, 13:01
Moin,

tschuldigung das ich den "alten" Tread wieder rauskram, aber ich wollt dafür keinen neuen neuen aufmachen..


wenn ich einem Codeblock per publicVari - eh vom server zu den Clients sende
funktioniert dies soweit, meine Frage ist kann ich lokale Variablenwerte, mitschicken?
also ohne diese in ein array zupacken und in der Funktion nochmal direkt auszulesen..


init.sqf
//server only
if (isServer) then
{
//serverseitige Funktionen
funcNvC = compile preprocessFile "BierAIG\server\funcNvC.sqf";//Function Nachricht vom Client
funcFahrzeugRespawn = compile preprocessFile "BierAIG\server\funcFahrzeugRespawn.sqf";//Function Fahrzeug Respawn
"NachrichtVomClient" addPublicVariableEventHandler
{
_nachricht = [_this] call funcNvC;
};//end addPublicVari-EH
};//end if-then

//clients only
if (!(isServer)) then
{
//clientseitige Funktionen
funcNvS = compile preprocessFile "BierAIG\client\funcNvS.sqf";//Function Nachricht vom Server
"NachrichtvomServer" addPublicVariableEventHandler
{
_nachricht = [_this] call funcNvS;
};//end addPublicVari-EH
};//end if-then

//server & client
funcFahrzeugAddKilledEH = compile preprocessFile "BierAIG\funcFahrzeugAddKilledEH.sqf";//Function Fahrzeug killed-EH geben
funcFahrzeugKilled = compile preprocessFile "BierAIG\funcFahrzeugKilled.sqf";//Function Fahrzeug killed
{
_eh = [_x] call funcFahrzeugAddKilledEH;
} foreach vehicles;

BierAIG\server\funcNvC.sqf
_wert = _this select 0;
_code = _wert select 1;

call (_code select 0);

BierAIG\server\funcFahrzeugRespawn.sqf
//server
_toter = _this select 0;
_killer = _this select 1;

//rauch und explosionen abwarten

_typetoter = typeof _toter;
_position = position _toter;
_richtung = getdir _toter;

_toter removealleventhandlers "killed";
deleteVehicle _toter;
_neuesFahrzeug = _typetoter createVehicle _position;
_neuesFahrzeug setpos _position;
_neuesFahrzeug setDir _richtung;
[_neuesFahrzeug] call funcFahrzeugAddKilledEH;

NachrichtVomServer = {[_neuesFahrzeug] call funcFahrzeugAddKilledEH;};
publicVariable "NachrichtVomServer";

BierAIG\client\funcNvS.sqf
_wert = _this select 0;
_code = _wert select 1;

call (_code select 0);

BierAIG\funcFahrzeugAddKilledEH.sqf
//server & client
_fahrzeug = _this select 0;
_fahrzeug addEventHandler ["killed", {_wert = [_this] call funcFahrzeugKilled;}];

BierAIG\funcFahrzeugKilled.sqf
//server & client
_wert = _this select 0;
_toter = _wert select 0;
_killer = _wert select 1;

if (!(isServer)) then
{
NachrichtVomClient = {[_toter,_killer] call funcFahrzeugRespawn;};
publicVariable "NachrichtVomClient";
};//end if-then

if (isServer) then
{
[_toter,_killer] call funcFahrzeugRespawn;
};//end if-then

wenn ich jetzt auf dedi-server nen Fahrzeug hochjage,welches bei mir aufm client lokal ist (kurz einsteigen), wird anscheinend das zerstörte objekt nicht zum server "mitgeschickt"(Habs fett markiert)..

ist es überhaupt möglich, in dem Codeblock Variablen reinzupacken?


Mfg
Langer

p.s. öhm funzt die Suchfunktion, hier im Forum nicht mehr?

.kju
30.05.2010, 14:02
Es scheint ein Verständnisproblem bei dir vorzuliegen.

{[_toter,_killer] call funcFahrzeugRespawn;};

1) Du übergibst deiner Funktion zwei lokale Paramater als Array.
2) Durch die Verwendung von call gibt die Funktion einen Wert zurück.

NachrichtVomClient = {[_toter,_killer] call funcFahrzeugRespawn;};

3) Komischerweise definierst du das Ergebnis als Type Code (durch {return})
und weißt es der GV NachrichtVomClient zu.

Statt dessen langt es vollkommen die Referenz des Fahrzeugs der GV zu übergeben.

4) Die Referenz des Fahrzeuge sollte anschließend an alle Clients übertragen werden.


Prüfe die Variableninhalt mit der Debug Console by Gaia (http://www.armaholic.com/page.php?id=5773) und diag_log (http://community.bistudio.com/wiki/diag_log).
Generelle Tipps: Community modding bible links (http://dev-heaven.net/projects/cmb/wiki)

N Beispiel:

vehicleRespawn.sqf

// add vehicle actions after respawn
AAS_PublicAddActionsAndEH = _vehicle;
publicVariable "AAS_PublicAddActionsAndEH";


init.sqf - Client only code:

"AAS_PublicAddActionsAndEH" addPublicVariableEventHandler
{
_vehicle = _this select 1;
[_vehicle] spawn cmdEnhanceViewDistance;

// clear vehicle cargo
clearWeaponCargo _vehicle;
clearMagazineCargo _vehicle;
};

Langer
30.05.2010, 16:11
(arma2 schafft mein "taschenrechner" net und fürn neuen fehlt momentan das geld:( daher "fummel" ich noch in arma 1 rum...)

jo ich weiss^^ ich hab nur die "standart-version" der funcFahrzeugKilled.sqf gepostet....

selbst wenn ich ein string übergebe, fehlen anscheinend die objekte für die call-anweisung



also im prinzip versuche ich, momentan, nen array zu übergeben.
dieses array soll aus nem Codeschnipsel, sowie aus den objekten,welche im codeschnipsel "benutzt" werden sollen, bestehen.

meine Frage ist, sofern möglich, wie müsste dieses array aussehen??

NachrichtVomClient = [{codeschnipzel},[wert1,wert2]];

in der funcNvC soll dann aus diesem übergebenem Array der codeschnipsel
ausgeführt werden

_komplett = _this select 0;
_array = _komplett select 1;
_code = _array select 0;

call _code;

der codeschnipsel müsste dann auf das (mit ihm) übergebene array zugreifen können..

{_array = NachrichtVomClient select 1;
_werte = _array select 1;
_wert1 = _werte select 0;
_wert2 = _werte select 1;
[_wert1,_wert2] call funcFahrzeugRespawn;}

das würde erheblich weniger publicVariable_eh's bedeuten,
nämlich nur zwei (einen für den server, einen für den client)
nicht für jede mögliche übergabe einen, wo nur nur die geforderten werte public werden, diese im EH ausgelesen und je nach zweck "verarbeitet" würden

das würde funcNvC/funcNvS universell einsetzbar machen..

was mir fehlt ist die schreibweise, also codeschnipsel und die beiden werte in ein array zu packen, sowie im codeschnipsel die werte einlesen...

wenn dies überhaupt möglich sein sollte ... grosses Fragezeichen

sorry falls ich des ein wenig "komisch" erlären sollte..

Mfg
Langer

.kju
30.05.2010, 17:27
Aus Erfahrung (Xeno, Sickboy, mir) spielt die Anzahl der PVEH keine Rolle.
Das Wichtigste ist möglichst wenig Daten übers Netwerk zu verschicken.

Insofern wenn du n generische PVEH unbedingt haben willst, dann schick einen
Identifier mit.

globalDefines.hpp
#define AAS_VEHICLERESPAWN 1

#include "globalDefines.hpp"
...
AAS_PVEH = [AAS_VEHICLERESPAWN,_vehicle];
publicVariable "AAS_PVEH";


#include "globalDefines.hpp"
...
"AAS_PVEH" addPublicVariableEventHandler
{

_identifier = (_this select 1) select 0;
switch (_identifier) do
{
case (AAS_VEHICLERESPAWN):
{
_vehicle = (_this select 1) select 1;
[_vehicle] spawn cmdEnhanceViewDistance;

// clear vehicle cargo
clearWeaponCargo _vehicle;
clearMagazineCargo _vehicle;
};
};
};

Langer
30.05.2010, 18:07
oh man...

bin ich auf die einfache Lösung mal wieder net gekommen.. 1000 Dank.. :daumen:

mit dieser Methode sind die "codeschnipsel" in einer Function und nicht wild über viele verteilt....

:angel:

Mfg
Langer