Einführung
Ich teile hier mal alles was ich bisher über Enforce, die Script Sprache auf der die DayZ Standalone und womöglich spätere Bohemia Titel basieren werden, herausgefunden habe.
Das ganze richtet sich an bereits erfahrene Programmierer, die die Grundkonzepte moderner Hochsprachen verstehen. Also falls ihr euch mit C#, C++ oder auch Java auskennt solltet ihr klarkommen. Falls nicht wird das ganze hier euch nicht wirklich weiterhelfen.
Ich kann nicht versichern das alles was ich hier schreibe zu 100% richtig ist, da es auf meinen eigenen getesteten Theorien basiert sowie auf Aussagen von Nutzern der DayZ Modding Discords.
Also falls jemand mehr weiß oder einen Fehler entdeckt kann er es ja posten.
Ich empfehle die DayZ Tools auf Steam unter Tools. In den DayZ Tools gibt es unter Workbench->Editors->Script Editor einen Script Editor mit Code highlighting und Codevorschlägen. Zum einrichten der Workbench für tatsächliche Mods (nicht nur modifizierte Missionfiles) diesen Guide verwenden.
Am Anfang empfiehlt es sich allerdings die dayZOffline.ChernarusPlus Mission im DayZ Verzeichnis->missions->init.c zu Editieren (vorher Backup machen!). Zum Starten von DayZ im offline mode braucht ihr eine batch Datei im Missionverzeichnis in der steht:
@echo off
taskkill /F /IM DayZ_x64.exe /T
RD /s /q "storage_-1" > nul 2>&1
cd ../../
start DayZ_x64.exe -mission=.\Missions\dayzOffline.ChernarusPlus -scrAllowFileWrite -nosplash -noPause -noBenchmark -name MyName -scriptDebug=true -filePatching -doLogs
(Batchdatei von Arkensor aus der CommunityOfflineMode)
Ganz wichtig dabei auch: DayZ SA windowed starten (einfach im Optionsmenü einstellen) so kann man bei Fehlern einfach Wiederholen drücken.
Wenn alles Funktioniert müsst ihr das Spiel auch nicht komplett neustarten um Änderungen in der Missionfile wirksam zu machen. Einfach dafür im Menü Neustarten drücken (manchmal ist trotzdem ein kompletter Neustart erforderlich z.B. beim bearbeiten von Dateien die mit #include eingebunden sind.).
Hilfreiche Links
- Lystic API Referenz (Stark Veraltet)
- Ingame Item Liste
- DayZ Modders Discord
- DayZ Discord
- Workbench Einrichten
- Meine Beispiele
Enforce Script - Grundlegende Konzepte
Enforce Script ist eine Scriptsprache die für die Enforce Engine entwickelt wurde.
Alles Scripts, auch die der grundlegenden Spiellogik sind in der scripts.pbo im dta Ordner des DayZSA verzeichnisses offen einsehbar und sind hilfreich um herauszufinden wie gewisse Sachen funktionieren (bis endlich eine ordentliche API referenz herauskommt).
Wichtige Eigenschaften:
- Objektorientiert
- Anweisungen werden mit einem ; (Semikolon) beendet.
- Funktionskörper/Kontrollstrukturenkörper in {} (Geschweiften Klammern)
- C Sprachen ähnlich
- Erstes Array Element ist 0
- Keine Mehrfachdeklarationen/Definitionen auf einer Zeile (z.B. int a,b;)
- Case sensitive (Groß und Kleinschreibung ist wichtig!)
- Big-endian (ggf. Systemabhängig)
- Für manches gibt es mehr als einen Syntax z.B. extends
- Nach Kontrollstrukturen können {} weggelassen werden wenn nur eine Anweisung kommt.
- Deklaration und Definition von Klassen und Funktionen immer gleichzeitig nie seperat (soweit ich weiß).
- Alles wird wie ein Objekt behandelt.
- Auf nicht initialisierte Objekte kann mit if(!e) geprüft werden.
- Klassen und Funktionen können auch verwendet werden vor der Zeile in der sie definiert werden.
Keywords
Liste ggf. unvollständig.
Keyword |
Beschreibung | Beispiel |
native | Von der Engine Implementierte Funktion kann nicht überschrieben werden. Im Quellcode der Engine sind solche Funktionen mit Offset vermerkt. Keyword derzeit für Modder belanglos. |
proto native UIScriptedMenu GetMenu(); |
proto | Funktionsprototyp (In C++ pure virtual function) ist nicht definiert und muss überschrieben werden. Nur für native Funktionen. |
proto int GetID(); |
ref |
Referenz auf Typ. Bei Basistypen crashed das Spiel. Verwendung bei Instanzen von Klassen empfohlen. |
ref array<int> iarr; |
reference | Für DayZ Tools (Material Editor) wird als parameter in material benutzt. Unklar. |
|
out | In Funktionsdeklaration Parameter das von Funktion modifiziert wird. |
void GetList(out array<int> _arr){} |
inout | In Funktionsdeklarartion Parameter das von Funktion benutzt und dann modifiziert wird. |
proto int strrep(inout string s, string sample, string replace); |
static | In Klasse: static member Variable/Funktion gibt es nur einmal für alle Instanzen(nützlich für Variable die Anzahl der Instanzen zählt). Wie in C++ In Funktionen: static Variable die auch nach Funktionsende bestehen bleibt (wie in C++). |
static int a; |
local | Als Variable / Funktionsparameter: Ist lokale Variable die nicht zu problemen führt bei mehrfachaufrufen der Funktion zur gleichen Zeit wie z.B. bei threads und Rekursion. |
local int a; |
for | Zählschleife wie in C++. |
for(int i=0;i<5;++i) {} |
while | While Schleife wie in C++. |
while(var) {} |
if, else |
Alternative wie in C++. |
if(a) {}else if(b) {}else {} |
class | Zum Definieren einer Klasse. |
class A{}; |
override | Zum überschreiben von Memberfunktionen einer parent-Klasse oder gemoddeter Klasse. |
override int GetId() {} |
private | Private Membervariable/funktion. Zugriff nur von Member der Klasse. |
private int m_a; |
protected | Protected Membervariable/funktion. Zugriff nur von Member der Klassenhierarchie. |
protected int m_b; |
modded | Modifizierte Gamelogik-Klasse. Zugriff auf Original mit super Keyword. |
modded class PlayerBase {} |
foreach | foreach Schleife. |
foreach(int a: iarr) {} |
true, false |
Boolsche Werte: Wahr, Falsch. |
bool a=true; |
null, NULL |
Wert einer nicht initialisierten Variable |
ref array<int> arr=null; |
new | Operator zum erstellen einer Instanz. |
ref array<int> arr=new array<int>; |
delete | Operator zum zerstören einer Instanz. (Wird vlt. selber aufgerufen wie garbage collector in Java?) |
delete arr; |
switch, case, default |
Switch wie in C++. Funktionalität muss ich noch genauer prüfen. |
switch(a){case 1:break;default:} |
continue | Wie in C++. In Schleifen: Derzeitigen Durchlauf abbrechen und mit nächsten anfangen. |
while(a) { if(b) continue; } |
break | Wie in C++. In Schleifen: Schleife abbrechen. |
while(a) { if(b) break; } |
enum | Aufzählung definieren. (Zuweisungen Optional) |
enum Keys {ESC=0,A=1,B=2}; |
super | Variable zum Zugriff auf Parent Klasse Funktionalität oder Zugriff auf die Originalklasse für modded Klassen. |
super.GetId(); |
return | In Funktion Rückgabe des Funktionswertes. |
return false; |
external |
Belanglos für Modder Funktion. Unklar. |
proto native external owned string GetName(); |
owned | Rückgabe wert darf nicht "released" werden. Unklar. |
proto native owned string ClassName(class ent); |
typedef | Type alias wie in C++. |
typedef array<string> TStringArray; |
extends | Alternative Schreibweise beim erben von Klassen. |
class A extends B {} //=class A : B {} |
volatile | Compiler Info ist Interne Funktion die wieder auf Script zurückgreift. Für Modding irrelevant. |
proto volatile Class Spawn(); |
this | In Klassen: Zugriff auf sich selbst. |
this.GetId(); |
const | Deklaration einer Konstanten oder als Parameter eines konstanten Parameters. |
const int a=0; |
goto |
Goto wie in C++. Sollte vlt. nicht genutzt werde. Noch nicht getestet. |
goto a; |
event | Info für DayZ Tools, dass diese Funktion entity Script event ist. |
event private void EOnTouch(IEntity other, int extra) |
notnull | Parameter darf nicht null sein. |
void InsertAll(notnull array<T> from) |
Datentypen
Basistypen
Typ |
Beschreibung | Beispiel |
bool | Boolscher Wert |
bool b=true; |
int | 32 Bit Integer |
int i=10; |
float | Fließkommazahl |
float f=0.5; |
string | Zeichenkette. In Zeichenketten wird der \ (Backslash) als Kontrollzeichen verwendet wie in C Sprachen. "\\" währe z.B. ein \ und "\n" ein Zeilenumbruch. |
string str="Test"; |
vector | 3xfloat Vektor |
vector vec="0.5 0 3" |
typ[] |
Einfaches Array vom Typ typ. Anzahl elemente Optional. Nutzung des Fortgeschrittenene Typs empfohlen. |
int a[3]={1,2,3}; |
Fortgeschrittene
Typ |
Beschreibung | Beispiel |
array<typ>, TTypArray |
Fortgeschrittener Array typ. TTypArray nur bei Basistypen wobei Typ der Basistype ist (sind typedefs). |
array<int> arr; |
map<typ1,typ2>, TTyp1Typ2Map |
Map Typ lässt sich indezieren mit typ1 und speichert Wert vom Typ typ2. TTyp1Typ2Map nur bei Basistypen wobei Typ der Basistype ist (sind typedefs). |
map<int,string> m; |
set<typ>, TTypSet |
Set Typ. TTypSet nur bei Basistypen wobei Typ der Basistype ist (sind typedefs). |
set<int> s; |
Sonstige
Typ | Beschreibung | Beispiel |
void | Leerer Typ bei Funktionen die nichts zurückgeben. |
void foo() {} |
auto | Typ der selbst den typ der zurückgegebenen Variable bestimmt. |
auto a=1; //a ist int |
typename | Typ eines types. Stichwort RTTI. |
typename t = int; |
func | Typ einer Funktion. |
void foo() {}func f=foo; |
Class | Typ für alle Klassen der basis funktionalität bietet. |
Class a = A; |
autoptr | Ist im Prinzip eine auto ref. Sollte eigentlich autoref heißen aber das ist von Bohemia Interactive. |
autoptr map<string, float> m_values; |
Alle Standardtypen sind auch als Klassen definiert.
Für die von der Engine definierten Memberfunktionen und Variablen Dateien EnScript.c, EnString.c und EnConvert.c in Verzeichnis scripts.pbo->1_Core->proto durchlesen.
Operatoren
Logik
Operator | Beschreibung | Beispiel |
<, > | Kleiner als, größer als. |
1<2 //true |
==, != | Gleich, ungleich. |
1==2 //false |
<=, >= |
Kleiner gleich, größer gleich. |
1<=1 //true |
! | Nicht. |
!true //false |
&&, || | Und, oder. |
true && false //falsetrue || false //true |
Arithmetisch
Operator | Beschreibung | Beispiel |
+, - |
Plus, Minus |
1+2 //=3 |
*, /, % |
Multiplikation, Division, Divisionsrest (nicht Modulus!). |
3*2 //=6 |
++, -- |
Inkrement, Dekrement |
int a=1;a++ //=1!!!++a //=3 |
Bitwise
Operator | Beschreibung | Beispiel |
&, | |
Und, Oder |
1 | 2 //=31 | 3 //=1 |
~ | Nicht |
~0 //=-1 |
<<, >> |
Linksbitshift, Rechtsbitshift |
1<<1 //=2 |
Sonstige
Operator | Beschreibung | Beispiel |
= | Zuweisungsoperator | a=1 |
[] | Index Operator (bei Arrays). |
int a[]={2,4,6};a[1] //=4 |
+=, -=, *=, /=, %= |
Kombinierte Operatoren. |
a+=1 //Entspricht: a=a+1 |
. | Zugriff auf Member in Klasse/Enum |
a.b |
Kommentare
Art | Beschreibung | Beispiel |
// | Einzeilliger Kommentar |
//Kommentar |
/* */ |
Mehrzeilliger Kommentar |
/*KommentarKommentar*/ |
Kontollstrukturen
Alternativen
if, else
if(a)
{
//Code wenn a wahr ist.
}
else if(b)
{
//Code wenn a falsch ist und b wahr.
}
else
{
//Code wenn a und b falsch sind.
}
Alles anzeigen
switch
switch(a)
{
case 1:
Code für a=1
break;
case 2:
//Code für a=2
break;
default:
//Code für Sonstige
}
Alles anzeigen
Schleifen
while
for
foreach
foreach(int a : arr) //arr vom typ array<int>
{
//a geht all member von arr durch
}
foreach(int a, string b : m) //m vom typ map<int,string>
{
//geht alle map Elemente durch. a ist der int teil und b der string Teil.
}
break, continue
while(true)
{
if(a) break; //führt solang code durch bis a true (das gleiche wie while(!a))
}
for(int i=1;i<=10;i++)
{
//Code wird bei jedem durchlauf durchgeführt.
if(i>=5) continue;
//Code wird nur bei Durchlauf 1 bis 4 durchgeführt.
}
Goto
Funktionen
int foo()
{
//Funktion die 1 Zurückgibt.
return 1;
}
int foo(int para) //Überladene Funktion
{
return para;
}
void bar(out array<int> b)
{
//Funktion die array modifiziert.
}
int func(int a = 1)
{
//Funktion mit Standardwert (Wird genommen beim auslassen dieses Parameters).
return a;
}
//Funktionsaufrufe
int v = foo();
bar(arr);
func();
func(2);
Alles anzeigen
Klassen
Klassen in Enforce Script stammen alle von der Klasse "Class" ab und haben damit immer die folgenden Memberfunktionen, die nicht überschrieben werden können:
Memberfunktion | Beschreibung |
bool IsInherited(typename type) |
Wahr wenn Klasse gleicher Typ wie type oder davon vererbter Typ. |
string ClassName() | Gibt Name der Klasse als String zurück. |
typename Type() | Gibt Typ zurück. |
string ToString() | Gibt Inhalt als string zurück. |
Class Cast(Class from) | Typecast von Klassen zwischen parent<->child |
bool CastTo(out Class to, Class from) | from wird zu Typ von to getypecasted und to zugewiesen. Wahr wenn erfolgreich. |
bool SafeCastType(Class type, out Class to, Class from) | Interne Funktion nicht für Modding relevant? |
Alle relevanten Arten von memberfunktionen/variablen sind im Beispiel. Am ende der Klassendefinition ist immer ein ; (Semikolon).
Beispiel:
class B
{
void bar(){}
void Bfoo() {}
}
class A : B //class A extends B ist das gleiche
{
private static m_cnt=0; //Static variable
private m_a; //Private Variable
protected m_b; //Protected Variable
void A(){m_cnt++;} //Konstruktor
void ~A(){m_cnt--;} //Destruktor
void foo()
{
//Funktion der Klasse
super.Bfoo(); //Zugriff auf Bfoo von parent Klasse B
DoSomething(this); //Funktion wird eigene Instanz übergeben.
}
override bar()
{
//überschriebene Funktion bar()
}
static int GetCnt() {return m_cnt;} //static funktion die Anzahl der Instanzen zurückgibt.
}
Alles anzeigen
Ref
Ref sorgt dafür, das eine Klasse als Referenz übergeben wird allerdings nicht immer.
Hier erläuterung von einem anderem Discord user (Jacob_Mango):
ref Class m_RefClass;
Class m_CopyClass;
void WillBeRef(Class class)
{
m_RefClass = class;
}
void WillBeRef(ref Class class)
{
m_RefClass = class;
}
void WillBeCopy(Class class)
{
m_CopyClass = class;
}
void WillBeCopy(ref Class class)
{
m_CopyClass = class;
}
Alles anzeigen
Anzumerken ist auch das Instanzen die vom Typ Object erben und dieser Typ selbst immer als Referenz übergeben werden da Sie direkt mit dem Objekt in der Spielwelt zusammenhängen.
Generell ist aber zu empfehlen alles als Referenz zu übergeben es sei denn eine Kopie ist erwünscht.
Bei Basis typen scheint das Keyword nicht zu funktionieren und crashed das Spiel!
Typecasts
Man man;
Object obj;
man=Man.Cast(obj); //Kann fehlschlagen da man von Objekt erbt
Class.CastTo(man,obj); //selbes wie Oben gibt true zurück wenn erfolgreich
man=(Object)obj; //wieder selbes anscheinend Funktionieren C Casts auch
Typecasts funktionieren nur zwischen Objekten die kompatibel sind. Etwas wie childclass->parentclass funktioniert immer parentclass->childclass aber nur wenn die Instanz auch tatsächlich eine childclass ist. So kann ein Spieler (Instanz von PlayerBase) nicht in eine Pflanze (PlantBase) umgewandelt werden auch wenn es der Spielstil mancher Spieler vermuten lässt.
Templates
Muss noch getestet werden geht vlt. auch mit Funktionen.
Nomenklatur der API
Das scheint die Nomenklatur zu sein (meist):
Art | Beschreibung | Beispiel |
Nicht öffentliche Membervariablen | Mit m_ beginnend. | m_player |
Funktionen | Jedes Wort mit Großbuchstaben beginnend. | GetGame |
Konstante | Alles Groß zwischen wörtern _ (Unterstrich) | COLOR_RED |
Klassenname | Jedes Wort mit Großbuchstaben beginned. | PlayerBase |
Variablen | Mit Kleinbuchstaben beginnend danach jedes Wort mit Großbuchstaben beginnend. | countPlayers |
Attribute
Anscheinend gibt es sowas wie spezielle Attribute wie NonSerialized() diese werden in [] geschrieben siehe scripts.pbo->1_Core->proto->Serializer.c für ein Beispiel.
Präprozessor
Präprozessor Anweisungen werden vor der Script ausgeführt wird geprüft und sind u.a. dazu da Scriptdateien dynamisch zu verändern z.B. je nach Platform. Sie Starten immer mit einer # (Raute) und sind im Grunde identisch zu C/C++.
Anweisung | Beschreibung | Beispiel |
#include | Bindet andere Script dateien ein (werden einfach eingefügt an dieser Stelle). Mit $CurrentDir: kann der Pfad der Spielinstallation eingefügt werden. Wichtig ist das der \ (Backslash) hier ein Kontrollzeichen im String ist und \\ dafür verwendet werden muss. |
#include "$CurrentDir:Missions\\dayzOffline.ChernarusPlus\\ref.c" |
#ifdef | Zweig für ist Definiert. |
#ifdef PLATFORM_CONSOLE |
#ifndef | Zweig für ist nicht Definiert. |
#ifndef PLATFORM_WINDOWS |
#else | Else zweig. |
#else |
#endif | Ende des Zweiges. |
#endif |
Defines (unvollständig)
Name | Zweck |
PLATFORM_WINDOWS | Spiel läuft auf Windows. |
GAME_TEMPLATE | Unklar. |
PLATFORM_CONSOLE | Läuft auf Konsole. |
PLATFORM_XBOX | Läuft auf XBOX |
PLATFORM_PS4 | Läuft auf PS4 |
NO_GUI | Unklar. |
DEVELOPER | Wahrscheinlich debug modus. |
SERVER_FOR_CONSOLE | Läuft auf Konsolenserver. |
BOT | Unklar. |
DAYZ_CREATURE_DEBUG_SHADOW | Unklar. |
BULDOZER | Wahrscheinlich in Buldozer gestartet. |
NEW_UI | Unklar. |
DOXYGEN | Wahrscheinlich Info für Doxygen. |
In API zurechtfinden
Die API ist nicht Dokumentiert und meist muss man selbst die Scripts durchsuchen. Dafür empfiehlt es sich die scripts.pbo im Dayz Verzeichnis->dta mit dem PBO Manager zu entpacken. Diesen Ordner kann dann mit Notepad++ über Suchen->In Datein Suchen durchsuchen. Das ist ganz hilfreich um Beispiele zur Anwendung verschiedener Klassen/Funktionen zu finden. Lystics API Referenz ist auch hilfreich um Informationen zu finden. Dafür einfach im Suchfeld ungefähr eingeben was man will und meist findet man dann auch was man sucht.
Mods von anderen Moddern können auch mit dem PBO Manager entpackt werden und deren Quelltext damit eingesehen.
Der oben genannte DayZ Modder Discord kann auch nach Stichworten durchsucht werden und im Notfall kann man dort auch mal fragen meist findet man dort was man will.