Chapter 1.13: Functions & Methods
Home | << Previous: Gotchas | Functions & Methods
Uvod
Functions are the fundamental unit of behavior in Enforce Script. Every action a mod performs --- spawning an item, checking a player's health, sending an RPC, drawing a UI element --- lives inside a function. Understanding how to declare them, pass data in and out, and work with the engine's special modifiers is essential for writing correct DayZ mods.
This chapter covers function mechanics in depth: declaration syntax, parameter passing modes, return values, default parameters, proto native bindings, static vs instance methods, overriding, the thread keyword, and the event keyword. If Chapter 1.3 (Classes) taught you where functions live, this chapter teaches you how they work.
Obsah
- Function Declaration Syntaxe
- Parametr Passing Modes
- Return Hodnotas
- Vychozi Parametr Hodnotas
- Limit parametrů: maximálně 16
- Proto Native Metodas (Engine Bindings)
- Static vs Instance Metodas
- Metoda Overriding
- Metoda Overloading (Not Supported)
- The event Keyword
- Thread Metodas (Coroutines)
- Deferred Calls with CallLater
- Doporucene postupy
- Pozorovano ve skutecnych modech
- Teorie vs praxe
- Caste chyby
- Tabulka rychle reference
Function Declaration Syntaxe
Every function has a return type, a name, and a parameter list. The body is enclosed in braces.
ReturnType FunctionName(ParamType paramName, ...)
{
// body
}Standalone Functions
Standalone (global) functions exist outside any class. They are rare in DayZ modding --- nearly all code lives inside classes --- but you will encounter a few in the vanilla scripts.
// Standalone function (global scope)
void PrintPlayerCount()
{
int count = GetGame().GetPlayers().Count();
Print(string.Format("Players online: %1", count));
}
// Standalone function with return value
string FormatTimestamp(int hours, int minutes)
{
return string.Format("%1:%2", hours.ToStringLen(2), minutes.ToStringLen(2));
}The vanilla engine defines several standalone utility functions:
// From enscript.c — helper for string expressions
string String(string s)
{
return s;
}Instance Metodas
The vast majority of functions in DayZ mods are instance methods --- they belong to a class and operate on that instance's data.
class LootSpawner
{
protected vector m_Position;
protected float m_Radius;
void SetPosition(vector pos)
{
m_Position = pos;
}
float GetRadius()
{
return m_Radius;
}
bool IsNearby(vector testPos)
{
return vector.Distance(m_Position, testPos) <= m_Radius;
}
}Instance methods have implicit access to this --- a reference to the current object. You rarely need to write this. explicitly, but it can help disambiguate when a parameter has a similar name.
Static Metodas
Static methods belong to the class itself, not to any instance. Call them via ClassName.Metoda(). They cannot access instance fields or this.
class MathHelper
{
static float Clamp01(float value)
{
if (value < 0) return 0;
if (value > 1) return 1;
return value;
}
static float Lerp(float a, float b, float t)
{
return a + (b - a) * Clamp01(t);
}
}
// Usage:
float result = MathHelper.Lerp(0, 100, 0.75); // 75.0Static methods are ideal for utility functions, factory methods, and singleton accessors. DayZ's vanilla code uses them extensively:
// From DamageSystem (3_game/damagesystem.c)
class DamageSystem
{
static bool GetDamageZoneMap(EntityAI entity, out DamageZoneMap zoneMap)
{
// ...
}
static string GetDamageDisplayName(EntityAI entity, string zone)
{
// ...
}
}Parametr Passing Modes
Enforce Script supports four parameter passing modes. Understanding them is critical because the wrong mode leads to silent bugs where data never reaches the caller.
By Hodnota (Vychozi)
When no modifier is specified, the parameter is passed by value. For primitives (int, float, bool, string, vector), a copy is made. Modifications inside the function do not affect the caller's variable.
void DoubleValue(int x)
{
x = x * 2; // modifies local copy only
}
// Usage:
int n = 5;
DoubleValue(n);
Print(n); // still 5 --- the original is unchangedFor class types (objects), by-value passing still passes a reference to the object --- but the reference itself is copied. You can modify the object's fields, but you cannot reassign the reference to point to a different object.
void RenameZone(SpawnZone zone)
{
zone.SetName("NewName"); // this WORKS --- modifies the same object
zone = null; // this does NOT affect the caller's variable
}out Parametry
The out keyword marks a parameter as output-only. The function writes a value into it, and the caller receives that value. The initial value of the parameter is undefined --- do not read it before writing.
// out parameter — function fills the value
bool TryFindPlayer(string name, out PlayerBase player)
{
array<Man> players = new array<Man>;
GetGame().GetPlayers(players);
for (int i = 0; i < players.Count(); i++)
{
PlayerBase pb = PlayerBase.Cast(players[i]);
if (pb && pb.GetIdentity() && pb.GetIdentity().GetName() == name)
{
player = pb;
return true;
}
}
player = null;
return false;
}
// Usage:
PlayerBase result;
if (TryFindPlayer("John", result))
{
Print(result.GetIdentity().GetName());
}The vanilla scripts use out extensively for engine-to-script data flow:
// From DayZPlayer (3_game/dayzplayer.c)
proto native void GetCurrentCameraTransform(out vector position, out vector direction, out vector rotation);
// From AIWorld (3_game/ai/aiworld.c)
proto native bool RaycastNavMesh(vector from, vector to, PGFilter pgFilter, out vector hitPos, out vector hitNormal);
// Multiple out parameters for look limits
proto void GetLookLimits(out float pDown, out float pUp, out float pLeft, out float pRight);inout Parametry
The inout keyword marks a parameter that is both read and written by the function. The caller's value is available inside the function, and any modifications are visible to the caller afterward.
// inout — the function reads the current value and modifies it
void ClampHealth(inout float health)
{
if (health < 0)
health = 0;
if (health > 100)
health = 100;
}
// Usage:
float hp = 150.0;
ClampHealth(hp);
Print(hp); // 100.0Vanilla examples of inout:
// From enmath.c — smoothing function reads and writes velocity
proto static float SmoothCD(float val, float target, inout float velocity[],
float smoothTime, float maxVelocity, float dt);
// From enscript.c — parsing modifies the input string
proto int ParseStringEx(inout string input, string token);
// From Pawn (3_game/entities/pawn.c) — transform is read and modified
event void GetTransform(inout vector transform[4])notnull Parametry
The notnull keyword tells the compiler (and the engine) that the parameter must not be null. If a null value is passed, the game will crash with an error rather than silently proceeding with invalid data.
void ProcessEntity(notnull EntityAI entity)
{
// Safe to use entity without null-checking — engine guarantees it
string name = entity.GetType();
Print(name);
}Vanilla uses notnull heavily in engine-facing functions:
// From envisual.c
proto native void SetBone(notnull IEntity ent, int bone, vector angles, vector trans, float scale);
proto native bool GetBoneMatrix(notnull IEntity ent, int bone, vector mat[4]);
// From DamageSystem
static bool GetDamageZoneFromComponentName(notnull EntityAI entity, string component, out string damageZone);You can combine notnull with out:
// From universaltemperaturesourcelambdabaseimpl.c
override void DryItemsInVicinity(UniversalTemperatureSourceSettings pSettings, vector position,
out notnull array<EntityAI> nearestObjects);Return Hodnotas
Single Return Hodnota
Functions return a single value. The return type is declared before the function name.
float GetDistanceBetween(vector a, vector b)
{
return vector.Distance(a, b);
}void (No Return)
Use void for functions that perform an action without returning data.
void LogMessage(string msg)
{
Print(string.Format("[MyMod] %1", msg));
}Returning Objects
When a function returns an object, it returns a reference (not a copy). The caller receives a pointer to the same object in memory.
EntityAI SpawnItem(string className, vector pos)
{
EntityAI item = EntityAI.Cast(GetGame().CreateObject(className, pos, false, false, true));
return item; // caller gets a reference to the same object
}Multiple Return Hodnotas via out Parametry
When you need to return more than one value, use out parameters. This is a universal pattern in DayZ scripting.
void GetTimeComponents(float totalSeconds, out int hours, out int minutes, out int seconds)
{
hours = (int)(totalSeconds / 3600);
minutes = (int)((totalSeconds % 3600) / 60);
seconds = (int)(totalSeconds % 60);
}
// Usage:
int h, m, s;
GetTimeComponents(3725, h, m, s);
// h == 1, m == 2, s == 5POZOR: JsonFileLoader Vraci void
A common trap: JsonFileLoader<T>.JsonLoadFile() returns void, not the loaded object. You must pass a pre-created object as a ref parameter.
// WRONG — will not compile
MyConfig config = JsonFileLoader<MyConfig>.JsonLoadFile(path);
// CORRECT — pass a ref object
MyConfig config = new MyConfig;
JsonFileLoader<MyConfig>.JsonLoadFile(path, config);Vychozi Parametr Hodnotas
Enforce Script supports default values for parameters. Parametry with defaults must come after all required parameters.
void SpawnItem(string className, vector pos, float quantity = -1, bool withAttachments = true)
{
// quantity defaults to -1 (full), withAttachments defaults to true
EntityAI item = EntityAI.Cast(GetGame().CreateObject(className, pos, false, false, true));
if (item && quantity >= 0)
item.SetQuantity(quantity);
}
// All of these are valid calls:
SpawnItem("AKM", myPos); // uses both defaults
SpawnItem("AKM", myPos, 0.5); // custom quantity, default attachments
SpawnItem("AKM", myPos, -1, false); // must specify quantity to reach attachmentsVychozi hodnoty z vanilla
The vanilla scripts use default parameters extensively:
// From Weather (3_game/weather.c)
proto native void Set(float forecast, float time = 0, float minDuration = 0);
proto native void SetDynVolFogDistanceDensity(float value, float time = 0);
// From UAInput (3_game/inputapi/uainput.c)
proto native float SyncedValue_ID(int action, bool check_focus = true);
proto native bool SyncedPress(string action, bool check_focus = true);
// From DbgUI (1_core/proto/dbgui.c)
static bool FloatOverride(string id, inout float value, float min, float max,
int precision = 1000, bool sameLine = true);
// From InputManager (2_gamelib/inputmanager.c)
proto native external bool ActivateAction(string actionName, int duration = 0);Omezeni
- Literal values only --- you cannot use expressions, function calls, or other variables as defaults:
// WRONG — no expressions in defaults
void MyFunc(float speed = Math.PI * 2) // COMPILE ERROR
// CORRECT — use a literal
void MyFunc(float speed = 6.283)- No named parameters --- you cannot skip a parameter by name. To set the third default, you must provide all preceding parameters:
void Configure(int a = 1, int b = 2, int c = 3) {}
Configure(1, 2, 10); // must specify a and b to set c
// There is no syntax like Configure(c: 10)- Vychozi values for class types are restricted to
nullorNULL:
void DoWork(EntityAI target = null, string name = "")
{
if (!target) return;
// ...
}Limit parametrů: maximálně 16
Metody v Enforce Scriptu nemohou mít více než 16 parametrů. Od DayZ 1.28 překročení tohoto limitu produkuje tvrdou chybu kompilace (předtím to způsobovalo tiché přetečení bufferu a náhodné pády):
// CHYBA KOMPILACE v 1.28+ — 17 parametrů přesahuje limit
void TooManyParams(int a, int b, int c, int d, int e, int f, int g, int h,
int i, int j, int k, int l, int m, int n, int o, int p,
int q)
{
}Řešení: Místo mnoha jednotlivých parametrů předejte třídu nebo pole:
class MyParams
{
int a, b, c, d, e;
float x, y, z;
string name;
}
void ProcessData(MyParams params)
{
// Přistupujte k params.a, params.b, atd.
}Proto Native Metodas (Engine Bindings)
Proto native methods are declared in script but implemented in the C++ engine. They form the bridge between your Enforce Script code and the DayZ game engine. You call them like normal methods, but you cannot see or modify their implementation.
Modifikator Reference
| Modifikator | Vyznam | Priklad |
|---|---|---|
proto native | Implemented in C++ engine code | proto native void SetPosition(vector pos); |
proto native owned | Vraci a value the caller owns (manages memory) | proto native owned string GetTyp(); |
proto native external | Defined in another module | proto native external bool AddSettings(typename cls); |
proto volatile | Has side effects; compiler must not optimize away | proto volatile int Call(Class inst, string fn, void parm); |
proto (without native) | Internal function, may or may not be native | proto int ParseString(string input, out string tokens[]); |
proto native
The most common modifier. These are straightforward engine calls.
// Setting/getting position (Object)
proto native void SetPosition(vector pos);
proto native vector GetPosition();
// AI pathfinding (AIWorld)
proto native bool FindPath(vector from, vector to, PGFilter pgFilter, out TVectorArray waypoints);
proto native bool SampleNavmeshPosition(vector position, float maxDistance, PGFilter pgFilter,
out vector sampledPosition);proto native owned
The owned modifier means the return value is allocated by the engine and ownership is transferred to the script. This is primarily used for string returns, where the engine creates a new string that the script's garbage collector must later free.
// From Class (enscript.c) — returns a string the script now owns
proto native owned external string ClassName();
// From Widget (enwidgets.c)
proto native owned string GetName();
proto native owned string GetTypeName();
proto native owned string GetStyleName();
// From Object (3_game/entities/object.c)
proto native owned string GetLODName(LOD lod);
proto native owned string GetActionComponentName(int componentIndex, string geometry = "");proto native external
The external modifier indicates the function is defined in a different script module. This allows cross-module method declarations.
// From Settings (2_gamelib/settings.c)
proto native external bool AddSettings(typename settingsClass);
// From InputManager (2_gamelib/inputmanager.c)
proto native external bool RegisterAction(string actionName);
proto native external float LocalValue(string actionName);
proto native external bool ActivateAction(string actionName, int duration = 0);
// From Workbench API (1_core/workbenchapi.c)
proto native external bool SetOpenedResource(string filename);
proto native external bool Save();proto volatile
The volatile modifier tells the compiler the function may have side effects or may call back into script (creating re-entrancy). The compiler must preserve full context when calling these.
// From ScriptModule (enscript.c) — dynamic function calls that may invoke script
proto volatile int Call(Class inst, string function, void parm);
proto volatile int CallFunction(Class inst, string function, out void returnVal, void parm);
// From typename (enconvert.c) — creates a new instance dynamically
proto volatile Class Spawn();
// Yielding control
proto volatile void Idle();Calling Proto Native Metodas
You call them like any other method. The key rule: never try to override or redefine a proto native method. They are fixed engine bindings.
// Calling proto native methods — no different from script methods
Object obj = GetGame().CreateObject("AKM", pos, false, false, true);
vector position = obj.GetPosition();
string typeName = obj.GetType(); // owned string — returned to you
obj.SetPosition(newPos); // native void — no returnStatic vs Instance Metodas
Kdy pouzit Static
Use static methods when the function does not need any instance data:
class StringUtils
{
// Pure utility — no state needed
static bool IsNullOrEmpty(string s)
{
return s == "" || s.Length() == 0;
}
static string PadLeft(string s, int totalWidth, string padChar = "0")
{
while (s.Length() < totalWidth)
s = padChar + s;
return s;
}
}Common static use cases:
- Utility functions --- math helpers, string formatters, validation checks
- Factory methods ---
Create()that returns a new configured instance - Singleton accessors ---
GetInstance()that returns the single instance - Konstanty/lookups ---
Init()+Cleanup()for static data tables
Singleton Vzor (Static + Instance)
Many DayZ managers combine static and instance:
class NotificationManager
{
private static ref NotificationManager s_Instance;
static NotificationManager GetInstance()
{
if (!s_Instance)
s_Instance = new NotificationManager;
return s_Instance;
}
// Instance methods for actual work
void ShowNotification(string text, float duration)
{
// ...
}
}
// Usage:
NotificationManager.GetInstance().ShowNotification("Hello", 5.0);Kdy pouzit Instance
Use instance methods when the function needs access to the object's state:
class SupplyDrop
{
protected vector m_DropPosition;
protected float m_DropRadius;
protected ref array<string> m_LootTable;
// Needs m_DropPosition, m_DropRadius — must be instance
bool IsInDropZone(vector testPos)
{
return vector.Distance(m_DropPosition, testPos) <= m_DropRadius;
}
// Needs m_LootTable — must be instance
string GetRandomItem()
{
return m_LootTable.GetRandomElement();
}
}Metoda Overriding
When a child class needs to change the behavior of a parent method, it uses the override keyword.
Basic Override
class BaseModule
{
void OnInit()
{
Print("[BaseModule] Initialized");
}
void OnUpdate(float dt)
{
// default: do nothing
}
}
class CombatModule extends BaseModule
{
override void OnInit()
{
super.OnInit(); // call parent first
Print("[CombatModule] Combat system ready");
}
override void OnUpdate(float dt)
{
super.OnUpdate(dt);
// custom combat logic
CheckCombatState();
}
}Pravidla for Overriding
overridekeyword is required --- without it, you create a new method that hides the parent's, instead of replacing it.Signature must match exactly --- same return type, same parameter types, same parameter count.
super.MetodaName()calls the parent --- use this to extend behavior rather than completely replace it.Private methods cannot be overridden --- they are invisible to child classes.
Protected methods can be overridden --- child classes see and can override them.
class Parent
{
private void SecretMethod() {} // cannot be overridden
protected void InternalWork() {} // can be overridden by children
void PublicWork() {} // can be overridden by anyone
}
class Child extends Parent
{
// override void SecretMethod() {} // COMPILE ERROR — private
override void InternalWork() {} // OK — protected is visible
override void PublicWork() {} // OK — public
}POZOR: Forgetting override
If you omit override, the compiler may emit a warning but will not error. Your method silently becomes a new method instead of replacing the parent's. The parent's version runs whenever the object is referenced through a parent-type variable.
class Animal
{
void Speak() { Print("..."); }
}
class Dog extends Animal
{
// BAD: Missing override — creates a NEW method
void Speak() { Print("Woof!"); }
// GOOD: Properly overrides
override void Speak() { Print("Woof!"); }
}Metoda Overloading (Not Supported)
Enforce Script does not support method overloading. You cannot have two methods with the same name but different parameter lists. Attempting this will cause a compile error.
class Calculator
{
// COMPILE ERROR — duplicate method name
int Add(int a, int b) { return a + b; }
float Add(float a, float b) { return a + b; } // NOT ALLOWED
}Workaround 1: Different Metoda Names
The most common approach is to use descriptive names:
class Calculator
{
int AddInt(int a, int b) { return a + b; }
float AddFloat(float a, float b) { return a + b; }
}Workaround 2: The Ex() Convention
DayZ vanilla and mods follow a naming convention where an extended version of a method appends Ex to the name:
// From vanilla scripts — base version vs extended version
void ExplosionEffects(Object source, Object directHit, int componentIndex);
void ExplosionEffectsEx(Object source, Object directHit, int componentIndex,
float energyFactor, float explosionFactor, HitInfo hitInfo);
// From EffectManager
static void EffectUnregister(Effect effect);
static void EffectUnregisterEx(Effect effect);
// From EntityAI
void SplitIntoStackMax(EntityAI destination_entity, int slot_id);
void SplitIntoStackMaxEx(EntityAI destination_entity, int slot_id);Workaround 3: Vychozi Parametry
If the difference is just optional parameters, use defaults instead:
class Spawner
{
// Instead of overloads, use defaults
void SpawnAt(vector pos, float radius = 0, string filter = "")
{
// one method handles all cases
}
}The event Keyword
The event keyword marks a method as an engine event handler --- a function that the C++ engine calls at specific moments (entity creation, animation events, physics callbacks, etc.). It is a hint for tooling (like Workbench) that the method should be exposed as a script event.
// From Pawn (3_game/entities/pawn.c)
protected event void OnPossess()
{
// called by engine when a controller possesses this pawn
}
protected event void OnUnPossess()
{
// called by engine when the controller releases this pawn
}
event void GetTransform(inout vector transform[4])
{
// engine calls this to get the entity's transform
}
// Event methods that supply data for networking
protected event void ObtainMove(PawnMove pMove)
{
// called by engine to gather movement input
}You typically override event methods in child classes rather than defining them from scratch:
class MyVehicle extends Transport
{
override event void GetTransform(inout vector transform[4])
{
// provide custom transform logic
super.GetTransform(transform);
}
}The key takeaway: event is a declaration modifier, not something you invoke. The engine calls event methods at the appropriate time.
Thread Metodas (Coroutines)
The thread keyword creates a coroutine --- a function that can yield execution and resume later. Despite the name, Enforce Script is single-threaded. Thread methods are cooperative coroutines, not OS-level threads.
Declaring and Starting a Thread
You start a thread by calling a function with the thread keyword preceding the call:
class Monitor
{
void Start()
{
thread MonitorLoop();
}
void MonitorLoop()
{
while (true)
{
CheckStatus();
Sleep(1000); // yield for 1000 milliseconds
}
}
}The thread keyword goes on the call, not the function declaration. The function itself is a normal function --- what makes it a coroutine is how you invoke it.
Sleep() and Yielding
Inside a thread function, Sleep(milliseconds) pauses execution and yields to other code. When the sleep time elapses, the thread resumes from where it left off.
Killing Threads
You can terminate a running thread with KillThread():
// From enscript.c
proto native int KillThread(Class owner, string name);
// Usage:
KillThread(this, "MonitorLoop"); // stops the MonitorLoop coroutineThe owner is the object that started the thread (or null for global threads). The name is the function name.
Kdy pouzit Threads (and When Not To)
Prefer CallLater and timers over threads. Thread coroutines have limitations:
- They are harder to debug (stack traces are less clear)
- They consume a coroutine slot that persists until completion or kill
- They cannot be serialized or transferred across network boundaries
Use threads only when you genuinely need a long-running loop with intermediate yields. For one-shot delayed actions, use CallLater (see below).
Deferred Calls with CallLater
CallLater schedules a function call to execute after a delay. It is the primary alternative to thread coroutines and is used extensively in vanilla DayZ.
Syntaxe
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(FunctionToCall, delayMs, repeat, ...params);| Parametr | Typ | Popis |
|---|---|---|
| Function | func | The method to call |
| Delay | int | Milliseconds before calling |
| Repeat | bool | true to repeat at interval, false for one-shot |
| Params | variadic | Parametry to pass to the function |
Call Categories
| Kategorie | Ucel |
|---|---|
CALL_CATEGORY_SYSTEM | General-purpose, runs every frame |
CALL_CATEGORY_GUI | UI-related callbacks |
CALL_CATEGORY_GAMEPLAY | Gameplay logic callbacks |
Priklady z vanilla
// One-shot delayed call (3_game/entities/entityai.c)
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DeferredInit, 34);
// Repeated call — login countdown every 1 second (3_game/dayzgame.c)
GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(this.LoginTimeCountdown, 1000, true);
// Delayed deletion with parameter (4_world/entities/explosivesbase)
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DeleteSafe, delayFor * 1000, false);
// UI delayed callback (3_game/gui/hints/uihintpanel.c)
m_Game.GetCallQueue(CALL_CATEGORY_GUI).CallLater(SlideshowThread, m_SlideShowDelay);Removing Queued Calls
To cancel a scheduled call before it fires:
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).Remove(FunctionToCall);Doporucene postupy
Keep functions short --- aim for under 50 lines. If a function is longer, extract helper methods.
Use guard clauses for early return --- check preconditions at the top and return early. This reduces nesting and makes the "happy path" easier to read.
void ProcessPlayer(PlayerBase player)
{
if (!player) return;
if (!player.IsAlive()) return;
if (!player.GetIdentity()) return;
// actual logic here, unnested
string name = player.GetIdentity().GetName();
// ...
}Prefer out parameters over complex return types --- when a function needs to communicate success/failure plus data, use a
boolreturn withoutparameters.Use static for stateless utilities --- if a method does not access
this, make itstatic. This documents intent and allows calling without an instance.Document proto native limitations --- when wrapping a proto native call, note in comments what the engine function can and cannot do.
Prefer CallLater over thread coroutines ---
CallLateris simpler, easier to cancel, and less error-prone.Always call super in overrides --- unless you intentionally want to completely replace the parent behavior. DayZ's deep inheritance chains depend on
supercalls propagating through the hierarchy.
Pozorovano ve skutecnych modech
Vzors confirmed by studying professional DayZ mod source code.
| Vzor | Mod | Detail |
|---|---|---|
TryGet___() returning bool with out param | COT / Expansion | Consistent pattern for nullable lookups: return true/false, fill out param on success |
MetodaEx() for extended signatures | Vanilla / Expansion Market | When an API needs more parameters, append Ex rather than breaking existing callers |
Static Init() + Cleanup() class methods | Expansion / VPP | Manager classes initialize static data in Init() and tear down in Cleanup(), called from mission lifecycle |
Guard clause if (!GetGame()) return at method start | COT Admin Tools | Every method that touches the engine starts with null checks to avoid crashes during shutdown |
Singleton GetInstance() with lazy creation | COT / Expansion / Dabs | Managers expose static ref instance with GetInstance() accessor, created on first access |
Teorie vs praxe
| Koncept | Teorie | Realita |
|---|---|---|
| Metoda overloading | Standard OOP feature | Not supported; use Ex() suffix or default parameters instead |
thread creates OS threads | Keyword suggests parallelism | Single-threaded coroutines with cooperative yielding via Sleep() |
out parameters are write-only | Should not read initial value | Some vanilla code reads the out param before writing; safer to always treat as inout defensively |
override is optional | Could be inferred | Omitting it silently creates a new method instead of overriding; always include it |
| Vychozi parameter expressions | Should support function calls | Only literal values (42, true, null, "") are allowed; no expressions |
Caste chyby
1. Forgetting override When Replacing a Parent Metoda
Without override, your method becomes a new method that hides the parent's. The parent's version will still be called when the object is referenced through a parent type.
// BAD — silently creates a new method
class CustomPlayer extends PlayerBase
{
void OnConnect() { Print("Custom!"); }
}
// GOOD — properly overrides
class CustomPlayer extends PlayerBase
{
override void OnConnect() { Print("Custom!"); }
}2. Expecting out Parametry to Be Pre-initialized
An out parameter has no guaranteed initial value. Never read it before writing.
// BAD — reading out param before it's set
void GetData(out int value)
{
if (value > 0) // WRONG — value is undefined here
return;
value = 42;
}
// GOOD — always write first, then read
void GetData(out int value)
{
value = 42;
}3. Trying to Overload Metodas
Enforce Script does not support overloading. Two methods with the same name cause a compile error.
// COMPILE ERROR
void Process(int id) {}
void Process(string name) {}
// CORRECT — use different names
void ProcessById(int id) {}
void ProcessByName(string name) {}4. Assigning the Return of a void Function
Some functions (notably JsonFileLoader.JsonLoadFile) return void. Trying to assign their result causes a compile error.
// COMPILE ERROR — JsonLoadFile returns void
MyConfig cfg = JsonFileLoader<MyConfig>.JsonLoadFile(path);
// CORRECT
MyConfig cfg = new MyConfig;
JsonFileLoader<MyConfig>.JsonLoadFile(path, cfg);5. Using Expressions in Vychozi Parametry
Vychozi parameter values must be compile-time literals. Expressions, function calls, and variable references are not allowed.
// COMPILE ERROR — expression in default
void SetTimeout(float seconds = GetDefaultTimeout()) {}
void SetAngle(float rad = Math.PI) {}
// CORRECT — literal values only
void SetTimeout(float seconds = 30.0) {}
void SetAngle(float rad = 3.14159) {}6. Forgetting super in Override Chains
DayZ's class hierarchies are deep. Omitting super in an override can break functionality several layers up the chain that you never even knew existed.
// BAD — breaks parent initialization
class MyMission extends MissionServer
{
override void OnInit()
{
// forgot super.OnInit() — vanilla initialization never runs!
Print("My mission started");
}
}
// GOOD
class MyMission extends MissionServer
{
override void OnInit()
{
super.OnInit(); // let vanilla + other mods initialize first
Print("My mission started");
}
}Tabulka rychle reference
| Funkce | Syntaxe | Poznamky |
|---|---|---|
| Instance method | void DoWork() | Has access to this |
| Static method | static void DoWork() | Called via ClassName.DoWork() |
| By-value param | void Fn(int x) | Copy for primitives; ref copy for objects |
out param | void Fn(out int x) | Write-only; caller receives value |
inout param | void Fn(inout float x) | Read + write; caller sees changes |
notnull param | void Fn(notnull EntityAI e) | Crashes on null |
| Vychozi value | void Fn(int x = 5) | Literals only, no expressions |
| Override | override void Fn() | Must match parent signature |
| Call parent | super.Fn() | Inside override body |
| Proto native | proto native void Fn() | Implemented in C++ |
| Owned return | proto native owned string Fn() | Script manages returned memory |
| External | proto native external void Fn() | Defined in another module |
| Volatile | proto volatile void Fn() | May callback into script |
| Event | event void Fn() | Engine-invoked callback |
| Thread start | thread MyFunc() | Starts coroutine (not OS thread) |
| Kill thread | KillThread(owner, "FnName") | Stops a running coroutine |
| Deferred call | CallLater(Fn, delay, repeat) | Preferred over threads |
Ex() convention | void FnEx(...) | Extended version of Fn |
Navigace
| Predchozi | Up | Dalsi |
|---|---|---|
| 1.12 Gotchas | Part 1: Enforce Script | -- |
