Skip to content

The 5-Layer Script Hierarchy ​


Summary: DayZ organizes all scripts into five compilation layers. Understanding these layers is the single most important concept in DayZ modding -- it determines where every file in your mod lives, what it can access, and when it executes.


Table of Contents ​


Overview ​

The DayZ engine compiles scripts in five distinct passes called script modules. Each module corresponds to a numbered folder in your mod's Scripts/ directory:

Scripts/
  1_Core/          --> engineScriptModule
  2_GameLib/       --> gameLibScriptModule
  3_Game/          --> gameScriptModule
  4_World/         --> worldScriptModule
  5_Mission/       --> missionScriptModule

Each layer builds on top of the previous ones. The numbers are not arbitrary -- they define a strict compilation and dependency order enforced by the engine.


The Layer Stack ​


Layer 1: 1_Core (engineScriptModule) ​

Purpose ​

The absolute foundation. Code here runs at the engine level before any game systems exist. This is the earliest point where mod code can execute.

What Goes Here ​

  • Constants and enums shared across all layers
  • Pure utility functions (math helpers, string utilities)
  • Logging infrastructure (the logger itself, not what logs)
  • Preprocessor defines and typedefs
  • Base class definitions that need to be visible everywhere

Worked Examples ​

Vanilla DayZ itself is the best illustration of what belongs at this level. The game's 1_core folder consists almost entirely of proto declarations -- script-side bindings for functions implemented in the engine's native code:

c
// Vanilla 1_core/proto/endebug.c -- engine function bindings
proto void Print(void var);
proto void PrintToRPT(void var);

The widget type IDs, WidgetFlags, math protos, and string utilities all live here too (1_core/proto/enwidgets.c, enmath.c, enstring.c). None of it knows anything about DayZ as a game -- it is pure engine surface.

A mod that puts something here follows the same spirit: engine-agnostic constants and enums with zero game dependencies. Throughout this wiki we use Lantern, a fictional framework mod built step by step in Part 7, as the running example:

c
// 1_Core/Lantern/LNT_Constants.c
class LNT_Constants
{
    static const string MOD_NAME    = "Lantern";
    static const string MOD_VERSION = "1.0.0";
};

// 1_Core/Lantern/LNT_LogLevel.c
enum LNT_LogLevel
{
    TRACE = 0,
    DEBUG = 1,
    INFO  = 2,
    WARN  = 3,
    ERROR = 4
};

When to Use ​

Use 1_Core only when you need something available to all other layers, and it has zero dependency on game types like PlayerBase, ItemBase, or MissionBase. Most mods do not need this layer at all.


Layer 2: 2_GameLib (gameLibScriptModule) ​

Purpose ​

Low-level engine library bindings. This layer exists in the vanilla script hierarchy but is rarely used by mods. It sits between the raw engine and the game logic.

What Goes Here ​

  • Engine-level abstractions (rendering, sound engine bindings)
  • Mathematical libraries beyond what 1_Core provides
  • Base widget/UI engine types

Worked Examples ​

The perfect vanilla example lives in 2_gamelib/tools.c: the ScriptCallQueue and ScriptInvoker classes that power every CallLater() timer and callback list in the game. Their signatures (abbreviated here) are almost entirely proto declarations -- thin script-side wrappers over native engine functionality:

c
// Vanilla 2_gamelib/tools.c (abbreviated)
class ScriptCallQueue
{
    proto native void Tick(float timeslice);
    proto void Call(func fn, void param1 = NULL);
    proto void CallLater(func fn, int delay = 0, bool repeat = false);
    proto void Remove(func fn);
};

class ScriptInvoker
{
    proto void Invoke(void param1 = NULL);
    proto bool Insert(func fn, int flags = EScriptInvokerInsertFlags.IMMEDIATE);
    proto bool Remove(func fn, int flags = EScriptInvokerRemoveFlags.ALL);
    proto native void Clear();
};

This is exactly why mods rarely touch gameLibScriptModule: the layer exists to bind engine services into script, and mods cannot add new native bindings. Anything you could write here in plain script works just as well in 3_Game, where the game types are also available.

When to Use ​

Almost never. Unless you are building a framework that needs engine-level bindings below the game layer, skip 2_GameLib entirely. The vast majority of mods use only layers 3, 4, and 5.


Layer 3: 3_Game (gameScriptModule) ​

Purpose ​

The workhorse layer for configuration, data definitions, and systems that do not interact directly with world entities. This is the first layer where game types are available.

What Goes Here ​

  • Configuration classes (settings that can be loaded/saved)
  • RPC registration and identifiers
  • Data classes and DTOs (data transfer objects)
  • Input binding registration
  • Plugin/module registration systems
  • Shared enums and constants that depend on game types
  • Custom keybind handlers

Worked Examples ​

Vanilla DayZ defines its own RPC identifiers here, in 3_game/enums/erpcs.c:

c
// Vanilla 3_game/enums/erpcs.c (abbreviated)
enum ERPCs
{
    RPC_SYNC_ITEM_VAR = 0,
    RPC_SYNC_STAT,
    RPC_WRITE_NOTE,
    RPC_WRITE_NOTE_CLIENT,
    RPC_SYNC_DISPLAY_STATUS,
    // ... the vanilla list continues
};

A mod does the same thing with its own identifier constants. Here is the Lantern version -- a plain constants class, fully usable from 3_Game, 4_World, and 5_Mission:

c
// 3_Game/Lantern/LNT_RPCIds.c
class LNT_RPCIds
{
    static const int RPC_REQUEST_STATUS = 1;
    static const int RPC_SEND_STATUS    = 2;
    static const int RPC_ADMIN_MESSAGE  = 3;
};

Configuration base classes with JSON persistence also live at this layer -- see Config Persistence for the full LNT_ConfigBase implementation.

When to Use ​

If in doubt, put it in 3_Game. This is the default layer for most non-entity code. Configuration classes, enums, constants, RPC definitions, data classes -- all belong here.


Layer 4: 4_World (worldScriptModule) ​

Purpose ​

Gameplay logic that interacts with the 3D world. This layer has access to entities, items, vehicles, buildings, and all world objects.

What Goes Here ​

  • Custom items and weapons (extending ItemBase, Weapon_Base)
  • Custom entities (extending Building, DayZAnimal, etc.)
  • World managers (spawn systems, loot managers, AI directors)
  • Player extensions (modded PlayerBase behavior)
  • Vehicle customization
  • Action systems (extending ActionBase)
  • Trigger zones and area effects

Worked Examples ​

Modding an existing entity is the most common 4_World job. This snippet is complete on its own -- drop it in 4_World/ and every player death gets logged:

c
// 4_World/Lantern/LNT_PlayerBase.c
modded class PlayerBase
{
    override void EEKilled(Object killer)
    {
        super.EEKilled(killer);
        Print("[Lantern] A player was killed");
    }
};

Vanilla DayZ defines all items here:

c
// Vanilla 4_world/entities/itembase/edible_base.c:1
class Edible_Base : ItemBase
{
    // All food items inherit from this
};

When to Use ​

Anything that touches the physical game world: creating entities, modifying items, handling player interactions, managing world state. If your class extends EntityAI, ItemBase, PlayerBase, Building, or interacts with GetGame().GetWorld(), it belongs in 4_World.


Layer 5: 5_Mission (missionScriptModule) ​

Purpose ​

The highest layer. Mission lifecycle, UI panels, HUD overlays, and the final initialization point. This is where client-side and server-side startup code lives.

What Goes Here ​

  • Mission class hooks (MissionServer, MissionGameplay overrides)
  • HUD and UI panels
  • Menu screens
  • Mod registration and initialization (the "boot" sequence)
  • Client-side rendering overlays
  • Server startup/shutdown handlers

Worked Examples ​

A framework mod hooks into the mission to initialize and shut down its subsystems. This is how the Lantern framework (built in Part 7) boots on the client:

c
// 5_Mission/Lantern/LNT_MissionGameplay.c
modded class MissionGameplay
{
    override void OnInit()
    {
        super.OnInit();
        LanternCore.Init();
    }

    override void OnMissionFinish()
    {
        LanternCore.ShutdownAll();
        super.OnMissionFinish();
    }
};

Vanilla DayZ builds its menu screens at this layer. 5_mission/gui/helpscreen.c is a compact reference for the pattern:

c
// Vanilla 5_mission/gui/helpscreen.c (abbreviated)
class HelpScreen extends UIScriptedMenu
{
    override Widget Init()
    {
        layoutRoot = g_Game.GetWorkspace().CreateWidgets("gui/layouts/help_screen.layout");
        // ... find widgets, fill list boxes
        return layoutRoot;
    }
};

A mod menu follows the exact same skeleton:

c
// 5_Mission/Lantern/GUI/LNT_StatusMenu.c
class LNT_StatusMenu : UIScriptedMenu
{
    protected TextWidget m_TitleText;

    override Widget Init()
    {
        layoutRoot = GetGame().GetWorkspace().CreateWidgets("Lantern_Core/GUI/layouts/status_menu.layout");
        m_TitleText = TextWidget.Cast(layoutRoot.FindAnyWidget("TitleText"));
        return layoutRoot;
    }

    override bool OnClick(Widget w, int x, int y, int button)
    {
        super.OnClick(w, x, y, button);
        return false;
    }
};

When to Use ​

UI, HUD, menu screens, and mod initialization that depends on the mission being active. Also the final place where the server hooks into startup/shutdown lifecycle.


The Critical Rule ​

Lower layers CANNOT reference types from higher layers.

This is the single most important rule in DayZ script architecture. The engine enforces this at compile time.

ALLOWED:
  5_Mission code references a class from 4_World       OK
  4_World code references a class from 3_Game           OK
  3_Game code references a class from 1_Core            OK

FORBIDDEN:
  3_Game code references a class from 4_World           COMPILE ERROR
  4_World code references a class from 5_Mission        COMPILE ERROR
  1_Core code references a class from 3_Game            COMPILE ERROR

Why This Exists ​

Each layer is compiled separately and sequentially. When 3_Game is being compiled, 4_World and 5_Mission do not exist yet. The compiler has no knowledge of those types.

What Happens When You Violate It ​

The error message is often unhelpful:

SCRIPT (E): Undefined type 'PlayerBase'

This typically means you placed code in 3_Game that references PlayerBase, which is defined in 4_World. The fix is to move your code to 4_World or higher.

The Workaround: Casting Through Base Types ​

When 3_Game code needs to handle an object that will be a PlayerBase at runtime, use the base Object or Man type (defined in 3_Game) and cast later:

c
// In 3_Game -- we cannot reference PlayerBase directly
class LNT_PlayerGreeter
{
    void HandlePlayer(Man player)
    {
        // 'Man' is available in 3_Game
        // At runtime, this will be a PlayerBase, but we cannot name it here
    }
};

// In 4_World -- now we can cast safely
class LNT_WorldLogic
{
    void ProcessPlayer(Man player)
    {
        PlayerBase pb;
        if (Class.CastTo(pb, player))
        {
            // Now we have full PlayerBase access
        }
    }
};

Load Order and Timing ​

What Controls Load Order ​

The only thing that determines mod load order is requiredAddons[] in config.cpp CfgPatches. Nothing else matters. Not the -mod= order on the command line, not folder names, not file names. The engine reads every PBO's CfgPatches, builds a dependency graph from requiredAddons[], and sorts mods topologically.

If your mod declares:

cpp
requiredAddons[] = { "DZ_Data", "Lantern_Core_Scripts" };

Then DZ_Data and Lantern_Core_Scripts are guaranteed to be loaded and compiled before your mod. If you forget to list a dependency, your mod may compile before it, causing "Undefined type" errors. Real frameworks publish their CfgPatches class names in their documentation -- always use the exact name the framework declares.

Compilation Order ​

The engine compiles all mods' scripts for each layer before moving to the next layer:

Step 1: Compile ALL mods' 1_Core scripts (ordered by requiredAddons)
Step 2: Compile ALL mods' 2_GameLib scripts (ordered by requiredAddons)
Step 3: Compile ALL mods' 3_Game scripts (ordered by requiredAddons)
Step 4: Compile ALL mods' 4_World scripts (ordered by requiredAddons)
Step 5: Compile ALL mods' 5_Mission scripts (ordered by requiredAddons)

Within each step, mods are ordered by the dependency graph built from requiredAddons[]. If ModB lists "ModA_Scripts" in its requiredAddons, ModA's scripts for that layer compile first.

Commonly reported, not documented: Some modders report that when two mods have no dependency relationship (neither lists the other in requiredAddons[]), they compile in ASCII alphabetical order of the CfgMods class name -- for example, class AlphaMod before class BetaMod. No Bohemia documentation describes a tie-breaking rule, so treat it as anecdotal rather than guaranteed engine behavior, and never design a mod that depends on it. If your code needs a specific order relative to another mod, declare that mod's CfgPatches class in requiredAddons[] -- that is the documented load-order mechanism, and the only one the engine enforces. You can observe the order on your own setup by putting a Print() call at global scope in each mod's 3_Game scripts and reading the order in the script log.

Initialization Order ​

After compilation, the runtime initialization follows a different sequence:

1. Engine boots, loads configs (requiredAddons determines config parse order)
2. 1_Core scripts are available (static constructors run)
3. 2_GameLib scripts are available
4. 3_Game scripts are available
   --> CfgMods entry functions run (e.g., "CreateGameMod")
   --> Input bindings register
5. 4_World scripts are available
   --> Entities can be created
6. Mission loads
7. 5_Mission scripts are available
   --> MissionServer.OnInit() / MissionGameplay.OnInit() fire
   --> UI and HUD become available

When Each Layer's Code Executes ​

LayerStatic InitRuntime ReadyKey Event
1_CoreFirstImmediatelyEngine boot
2_GameLibSecondAfter engine initEngine subsystems ready
3_GameThirdAfter game initCreateGame() / custom entry function
4_WorldFourthAfter world loadsEntities start spawning
5_MissionFifth (last)After mission startsMissionServer.OnInit() / MissionGameplay.OnInit()

Important: Static variables and global-scope code in each layer execute during the compilation/linking phase, before OnInit() is ever called. Do not put complex initialization logic in static initializers.


Practical Guidelines ​

"If in Doubt, Put It in 3_Game" ​

This is the most common layer for mod code. Unless your code:

  • Needs to be available before game types exist --> 1_Core
  • Extends an entity/item/vehicle/player --> 4_World
  • Touches UI, HUD, or mission lifecycle --> 5_Mission

...then it belongs in 3_Game.

The Layer Checklist ​

Before placing a file, ask these questions:

  1. Does it extend EntityAI, ItemBase, PlayerBase, Building, or any world entity? Put it in 4_World.

  2. Does it reference MissionServer, MissionGameplay, or create UI widgets? Put it in 5_Mission.

  3. Is it a pure data class, config, enum, or RPC definition? Put it in 3_Game.

  4. Is it a fundamental constant or utility with zero game dependencies? Put it in 1_Core.

  5. None of the above? Default to 3_Game.

Keep Your Layers Thin ​

A common mistake is dumping everything into 4_World. This creates tightly coupled code. Instead:

GOOD:
  3_Game/  --> Config class, enums, RPC IDs, data structs
  4_World/ --> Manager that uses the config, entity classes
  5_Mission/ --> UI that displays manager state

BAD:
  4_World/ --> Config, enums, RPCs, managers, AND entity classes all mixed together

Quick Decision Guide ​


Common Mistakes ​

1. Referencing PlayerBase from 3_Game ​

c
// WRONG: in 3_Game/LNT_ZoneConfig.c
class LNT_ZoneConfig
{
    void ApplyToPlayer(PlayerBase player)  // ERROR: PlayerBase not defined yet
    {
    }
};

// RIGHT: in 3_Game/LNT_ZoneConfig.c
class LNT_ZoneConfig
{
    ref array<float> m_Values;  // Pure data, no entity references
};

// RIGHT: in 4_World/LNT_ZoneManager.c
class LNT_ZoneManager
{
    void ApplyConfig(PlayerBase player, LNT_ZoneConfig config)
    {
        // Now we can use both
    }
};

2. Putting UI Code in 4_World ​

c
// WRONG: in 4_World/LNT_AdminPanel.c
class LNT_AdminPanel : UIScriptedMenu  // UIScriptedMenu is declared in 3_Game
{                                       // (3_game/tools/uiscriptedmenu.c:66), so this
                                        // compiles -- but the MissionGameplay hooks that
                                        // open and close a menu live in 5_Mission
    // This will cause problems when trying to register the UI
};

// RIGHT: in 5_Mission/LNT_AdminPanel.c
class LNT_AdminPanel : UIScriptedMenu
{
    // UI belongs in 5_Mission where mission lifecycle is available
};

3. Putting Constants in 4_World When 3_Game Needs Them ​

c
// WRONG: Constants defined in 4_World
// 4_World/LNT_Constants.c
const int LNT_RPC_ID = 12345;

// 3_Game/LNT_RPCHandler.c
class LNT_RPCHandler
{
    void Register()
    {
        // ERROR: LNT_RPC_ID not visible here (defined in higher layer)
    }
};

// RIGHT: Constants defined in 3_Game (or 1_Core)
// 3_Game/LNT_Constants.c
const int LNT_RPC_ID = 12345;  // Now visible to 3_Game AND 4_World AND 5_Mission

4. Overcomplicating with 1_Core ​

If your "constants" reference any game type, they cannot go in 1_Core. Even something like const string PLAYER_CONFIG_PATH is fine in 1_Core, but a class that takes a CGame parameter is not.


Summary ​

LayerFolderConfig EntryPrimary UseFrequency
11_Core/engineScriptModuleConstants, utilities, logging baseRare
22_GameLib/gameLibScriptModuleEngine bindingsVery rare
33_Game/gameScriptModuleConfigs, RPCs, data classesMost common
44_World/worldScriptModuleEntities, items, managersCommon
55_Mission/missionScriptModuleUI, HUD, mission hooksCommon

Remember: Lower layers cannot see higher layers. When in doubt, use 3_Game. Move code up only when you need access to types defined in a higher layer.

Released under CC BY-SA 4.0 | Code examples under MIT License