Complete Examples

Example: Registering All Callback Types

Here's a complete example showing how to register all callback types in your mod's initialization:

// In your mod's MissionServer.c file
modded class MissionServer
{
    override void OnInit()
    {
        super.OnInit();
        
        // Register callbacks for zone events (for testing/debugging or production use)
        MyZoneEventCallback zoneCallback = new MyZoneEventCallback();
        MyPlayerStateChangeCallback stateCallback = new MyPlayerStateChangeCallback();
        MyExitTimerCallback timerCallback = new MyExitTimerCallback();
        
        // Register server-side callbacks
        PlayerZoneController.RegisterZoneEventCallback(zoneCallback);
        PlayerZoneController.RegisterPlayerStateChangeCallback(stateCallback);
        PlayerZoneController.RegisterExitTimerCallback(timerCallback);
        
        Print("[MyMod] All server-side callbacks registered successfully (zone, state, timer).");
    }
}

// For client-side map drawing in MissionGameplay.c
modded class MissionGameplay
{
    override void OnInit()
    {
        super.OnInit();
        
        // Register drawing callback on client side
        MyZoneDrawingCallback drawingCallback = new MyZoneDrawingCallback();
        MapDrawer.RegisterZoneDrawingCallback(drawingCallback);
        
        Print("[MyMod] Drawing callback registered successfully.");
    }
}

Example 1: Sound Mod Integration

This example shows how to play a sound when a player enters/exits a specific zone. This is a complete, working implementation based on the NinjinsSoundMod test mod.

Important: DayZ requires CfgSoundShaders and CfgSoundSets (not CfgSounds) for SEffectManager.PlaySound() to work correctly. The sound name must end with _SoundSet.

Step 1: Configure Sounds in config.cpp

File: MySoundMod/config.cpp

class CfgSoundShaders
{
	class MySoundMod_SoundShader_ZoneEnter
	{
		samples[] = { {"MySoundMod\sounds\zone_enter.ogg", 1} };
		range = 30;        // Sound radius in meters
		volume = 1.0;      // Volume level (0.0 to 1.0)
		rangeCurve[] = {
			{0, 1},        // At 0m: 100% volume
			{15, 0.5},     // At 15m: 50% volume
			{30, 0.1}      // At 30m: 10% volume
		};
	};
	
	class MySoundMod_SoundShader_ZoneExit
	{
		samples[] = { {"MySoundMod\sounds\zone_exit.ogg", 1} };
		range = 30;
		volume = 1.0;
		rangeCurve[] = {
			{0, 1},
			{15, 0.5},
			{30, 0.1}
		};
	};
};

class CfgSoundSets
{
	class MySoundMod_SoundZoneEnter_SoundSet
	{
		soundShaders[] = {"MySoundMod_SoundShader_ZoneEnter"};
	};
	
	class MySoundMod_SoundZoneExit_SoundSet
	{
		soundShaders[] = {"MySoundMod_SoundShader_ZoneExit"};
	};
}

Note:

Step 2: Create Zone Callback (Server-side)

File: MySoundMod/scripts/5_Mission/ZoneSoundCallback.c

class ZoneSoundCallback : ZoneEventCallback
{
    override void OnZoneEnter(PlayerBase player, ZoneBase zone)
    {
        if (!player || !zone)
            return;
        
        string zoneName = zone.GetName();
        
        // Only play sound for specific zone name
        if (zoneName != "SoundZone")
            return;
        
        string playerName = "Unknown";
        if (player.GetIdentity())
            playerName = player.GetIdentity().GetName();
        
        Print("[ZoneSoundCallback] Player " + playerName + " entered zone: " + zoneName);
        
        // Send RPC to client to play enter sound
        PlayerIdentity playerIdentity = player.GetIdentity();
        if (playerIdentity)
        {
            GetRPCManager().SendRPC("MySoundMod", "PlayZoneEnterSound", new Param1(zoneName), true, playerIdentity);
        }
    }
    
    override void OnZoneExit(PlayerBase player, ZoneBase zone)
    {
        if (!player || !zone)
            return;
        
        string zoneName = zone.GetName();
        
        // Only play sound for specific zone name
        if (zoneName != "SoundZone")
            return;
        
        string playerName = "Unknown";
        if (player.GetIdentity())
            playerName = player.GetIdentity().GetName();
        
        Print("[ZoneSoundCallback] Player " + playerName + " exited zone: " + zoneName);
        
        // Send RPC to client to play exit sound
        PlayerIdentity playerIdentity = player.GetIdentity();
        if (playerIdentity)
        {
            GetRPCManager().SendRPC("MySoundMod", "PlayZoneExitSound", new Param1(zoneName), true, playerIdentity);
        }
    }
}

Step 3: Register Callback in MissionServer.c (Server-side)

File: MySoundMod/scripts/5_Mission/MissionServer.c

modded class MissionServer extends MissionBase
{
	private ref ZoneSoundCallback m_ZoneSoundCallback;
	
	override void OnInit()
	{
		super.OnInit();
		
		// Register zone event callback for sound playback
		m_ZoneSoundCallback = new ZoneSoundCallback();
		PlayerZoneController.RegisterZoneEventCallback(m_ZoneSoundCallback);
		
		Print("[MySoundMod] Zone sound callback registered successfully.");
	}
}

Step 4: Handle RPCs and Play Sounds (Client-side)

File: MySoundMod/scripts/5_Mission/missionGameplay.c

modded class MissionGameplay extends MissionBase
{
	override void OnInit()
	{
		super.OnInit();
		
		// Register RPCs for sound playback
		GetRPCManager().AddRPC("MySoundMod", "PlayZoneEnterSound", this, SingleplayerExecutionType.Client);
		GetRPCManager().AddRPC("MySoundMod", "PlayZoneExitSound", this, SingleplayerExecutionType.Client);
		
		Print("[MySoundMod] RPC handlers registered successfully.");
	}
	
	// RPC handler for zone enter sound
	void PlayZoneEnterSound(CallType type, ParamsReadContext ctx, PlayerIdentity sender, Object target)
	{
		Param1 data;
		if (!ctx.Read(data))
			return;
		
		string zoneName = data.param1;
		
		// Get the local player (use safe casting)
		PlayerBase player = PlayerBase.Cast(GetGame().GetPlayer());
		if (!player)
			return;
		
		// Play sound at player's position
		// IMPORTANT: Use the SoundSet name (ends with _SoundSet), not the file path
		string soundPath = "MySoundMod_SoundZoneEnter_SoundSet";
		vector playerPos = player.GetPosition();
		
		// Play 3D sound at player position
		SEffectManager.PlaySound(soundPath, playerPos);
		
		Print("[MySoundMod] Playing enter sound for zone: " + zoneName);
	}
	
	// RPC handler for zone exit sound
	void PlayZoneExitSound(CallType type, ParamsReadContext ctx, PlayerIdentity sender, Object target)
	{
		Param1 data;
		if (!ctx.Read(data))
			return;
		
		string zoneName = data.param1;
		
		// Get the local player (use safe casting)
		PlayerBase player = PlayerBase.Cast(GetGame().GetPlayer());
		if (!player)
			return;
		
		// Play sound at player's position
		string soundPath = "MySoundMod_SoundZoneExit_SoundSet";
		vector playerPos = player.GetPosition();
		
		// Play 3D sound at player position
		SEffectManager.PlaySound(soundPath, playerPos);
		
		Print("[MySoundMod] Playing exit sound for zone: " + zoneName);
	}
}

Complete File Structure

MySoundMod/
├── config.cpp                              (Contains CfgSoundShaders and CfgSoundSets)
├── sounds/
│   ├── zone_enter.ogg                      (Your sound file)
│   └── zone_exit.ogg                      (Your sound file)
└── scripts/
    └── 5_Mission/
        ├── MissionServer.c                 (Server-side: Register callback)
        ├── missionGameplay.c               (Client-side: RPC handlers)
        └── ZoneSoundCallback.c             (Server-side: Callback implementation)

Key Points

  1. Sound Configuration: Use CfgSoundShaders and CfgSoundSets, not CfgSounds
  2. SoundSet Naming: SoundSet names must end with _SoundSet suffix
  3. Sound Path: Use the SoundSet name (e.g., "MySoundMod_SoundZoneEnter_SoundSet"), not the file path
  4. Safe Casting: Always use PlayerBase.Cast() when getting the player
  5. Ref Keyword: Use ref for callback member variables in MissionServer
  6. Zone Filtering: Check zone name in the callback if you only want sounds for specific zones

This implementation has been tested and works correctly with NinjinsPvPPvE.

Example 2: Custom Map Drawing

// CustomMapOverlay.c
class CustomMapOverlayCallback : ZoneDrawingCallback
{
    override void OnAfterZonesDrawn(MapDrawer mapDrawer, array zones)
    {
        if (!mapDrawer || !zones)
            return;
        
        // Draw a custom event zone
        vector eventCenter = "6000 0 6000";
        float eventRadius = 500.0;
        int eventColor = ARGB(200, 255, 165, 0); // Orange, semi-transparent
        
        mapDrawer.DrawCircle(eventCenter, eventRadius, eventColor, true, false, 10);
        mapDrawer.DrawLabel(eventCenter, "Event Zone", ARGB(255, 255, 255, 255));
        
        // Draw a custom polygon
        array polygonVertices = {
            "5500 0 5500",
            "5500 0 6500",
            "6500 0 6500",
            "6500 0 5500"
        };
        int polyColor = ARGB(180, 0, 255, 255); // Cyan, more transparent
        mapDrawer.DrawPolygon(polygonVertices, polyColor, false, false, 8);
    }
}

// In MissionGameplay.c or map menu Init
void CustomMapOverlayInit()
{
    CustomMapOverlayCallback callback = new CustomMapOverlayCallback();
    MapDrawer.RegisterZoneDrawingCallback(callback);
}

Example 3: UI Mod with State Changes

// UIMod.c
class UIModStateCallback : PlayerStateChangeCallback
{
    override void OnPlayerEnteredPvP(PlayerBase player)
    {
        if (!player || player != GetGame().GetPlayer())
            return;
        
        // Update UI to show PvP mode
        // Your UI update code here
        Print("[UIMod] PvP mode activated!");
    }
    
    override void OnPlayerExitedPvP(PlayerBase player)
    {
        if (!player || player != GetGame().GetPlayer())
            return;
        
        // Update UI to show normal mode
        Print("[UIMod] PvP mode deactivated!");
    }
    
    // Implement other methods...
}

// In MissionServer.c
void UIModInit()
{
    UIModStateCallback callback = new UIModStateCallback();
    PlayerZoneController.RegisterPlayerStateChangeCallback(callback);
}

Example 4: No Vehicle Zone

Teleport Players out of zone to Green Mountain while in disallowed vehicleType.

This example shows how to detect when players enter a specific zone and teleport them (and their vehicle) to a safe location if they're in a disallowed vehicle type. Players on foot or in allowed vehicles are not teleported.

File: NinjinsCallBackTestMod/scripts/5_Mission/NinjinsCallBackTest.c

// Handles zone enter/exit events and teleports the player to Green Mountain

class NinjinsCallBackTest : ZoneEventCallback
{
    override void OnZoneEnter(PlayerBase player, ZoneBase zone)
    {
        if (!player || !zone)
            return;
        
        string zoneName = zone.GetName();
        
        // Only teleport player for specific zone name
        if (zoneName != "SoundZone")
            return;
        
        string playerName = "Unknown";
        if (player.GetIdentity())
            playerName = player.GetIdentity().GetName();
        
        Print("[NinjinsCallBackTest] Player " + playerName + " entered zone: " + zoneName);
        
        // Teleport player to Green Mountain
        vector greenMountainPos = "3710.86 402 5998.8";
        
        // List of disallowed vehicles, players in these vehicles WILL be teleported out
        array disallowedVehicles = {
            "Boat_01_Orange",
            "OffroadHatchback",
            "CivilianSedan",
            "Truck_01_Covered"
        };
        
        // Handle vehicle teleportation, teleport OUT if player is IN a disallowed vehicle
        HumanCommandVehicle hcv;
        Transport transport;
        bool isDisallowed = false;
        string vehicleType = "";
        
        if (player.IsInTransport())
        {
            hcv = player.GetCommand_Vehicle();
            if (hcv)
            {
                transport = hcv.GetTransport();
                if (transport)
                {
                    vehicleType = transport.GetType();
                    
                    // Check if vehicle is in the disallowed list
                    for (int i = 0; i < disallowedVehicles.Count(); i++)
                    {
                        if (vehicleType == disallowedVehicles[i])
                        {
                            isDisallowed = true;
                            break;
                        }
                    }
                    
                    if (isDisallowed)
                    {
                        // Player is in a disallowed vehicle, teleport them out (teleport the vehicle with player)
                        Print("[NinjinsCallBackTest] Player " + playerName + " is in disallowed vehicle (" + vehicleType + ") - TELEPORTING OUT to Green Mountain");
                        transport.SetPosition(greenMountainPos);
                    }
                    else
                    {
                        // Player is in a vehicle but not a disallowed one, don't teleport
                        Print("[NinjinsCallBackTest] Player " + playerName + " is in vehicle (" + vehicleType + ") but it's not disallowed - NOT teleporting");
                    }
                }
            }
        }
        else
        {
            // Player is not in a vehicle, don't teleport
            Print("[NinjinsCallBackTest] Player " + playerName + " is not in a vehicle - NOT teleporting");
        }
    }
}

File: NinjinsCallBackTestMod/scripts/5_Mission/MissionServer.c

modded class MissionServer extends MissionBase
{
	private ref NinjinsCallBackTest m_NinjinsCallBackTest;
	
	void ~MissionServer() {
	}

	void MissionServer()
	{
		Print("NinjinsCallBackTestMod mod has started !");
	}
	
	override void OnInit()
	{
		super.OnInit();
		
		// Register zone event callback for teleportation
		m_NinjinsCallBackTest = new NinjinsCallBackTest();
		PlayerZoneController.RegisterZoneEventCallback(m_NinjinsCallBackTest);
		
		Print("[NinjinsCallBackTestMod] Zone callback registered successfully.");
	}
};

Key Points:

  1. Vehicle Detection: Uses player.IsInTransport() to check if player is in a vehicle
  2. Vehicle Type Checking: Compares vehicle type against a disallowed list
  3. Teleportation: Teleports the entire vehicle (with player inside) using transport.SetPosition()
  4. Zone Filtering: Only processes events for the specific zone name "SoundZone"
  5. Safe Teleportation: Players on foot or in allowed vehicles are not affected

Example 5: Allow Damage in PvE Zones

This example shows how to override PvE zone protection to allow damage even when players are in PvE zones. This can be useful for special events, admin tools, or custom gameplay mechanics.

File: NinjinsCallBackTestMod/scripts/5_Mission/NinjinsCallBackTest.c

// Example damage callback - allows damage even in PvE zones

class NinjinsCallBackTest : DamageCallback
{
    override bool OnShouldAllowDamageOverride(PlayerBase victim, PlayerBase attacker, TotalDamageResult damageResult, int damageType, EntityAI source, int component, string dmgZone, string ammo, vector modelPos, float speedCoef, bool shouldAllowDamageResult)
    {
        // This callback runs AFTER ShouldAllowDamage makes its decision
        // Return true to FORCE allow damage (override ShouldAllowDamage's decision)
        // Return false (or shouldAllowDamageResult) to respect ShouldAllowDamage's decision
        
        if (!victim)
            return shouldAllowDamageResult;
        
        // Debug: Log that override callback was called
        Print("[NinjinsCallBackTest] OnShouldAllowDamageOverride called - shouldAllowDamageResult: " + shouldAllowDamageResult + ", victim in PvE: " + victim.netSync_IsInPvEZone);
        
        // Check if damage was blocked and victim is in PvE zone
        // If so, override to allow damage even in PvE
        if (!shouldAllowDamageResult && victim.netSync_IsInPvEZone)
        {
            string victimName = "Unknown";
            if (victim.GetIdentity())
                victimName = victim.GetIdentity().GetName();
            
            string attackerName = "Unknown";
            if (attacker && attacker.GetIdentity())
                attackerName = attacker.GetIdentity().GetName();
            
            Print("[NinjinsCallBackTest] ===== OVERRIDING PvE PROTECTION =====");
            Print("[NinjinsCallBackTest] Victim: " + victimName + " (in PvE zone)");
            Print("[NinjinsCallBackTest] Attacker: " + attackerName);
            Print("[NinjinsCallBackTest] ALLOWING damage despite PvE protection");
            Print("[NinjinsCallBackTest] =====================================");
            
            // Force allow damage even in PvE zone
            return true;
        }
        
        // For all other cases, respect ShouldAllowDamage's decision
        return shouldAllowDamageResult;
    }
    
    override bool OnShouldAllowItemDamageOverride(ItemBase item, PlayerBase attacker, TotalDamageResult damageResult, int damageType, EntityAI source, string dmgZone, string ammo, vector modelPos, float speedCoef, bool shouldAllowItemDamageResult)
    {
        // This callback runs AFTER ShouldAllowItemDamage makes its decision
        // Return true to FORCE allow damage (override ShouldAllowItemDamage's decision)
        // Return false (or shouldAllowItemDamageResult) to respect ShouldAllowItemDamage's decision
        
        if (!item)
            return shouldAllowItemDamageResult;
        
        string itemType = item.GetType();
        
        string attackerName = "Unknown";
        if (attacker && attacker.GetIdentity())
            attackerName = attacker.GetIdentity().GetName();
        
        PlayerBase owner = item.GetOwnerPlayer();
        string ownerName = "None";
        bool ownerInPvE = false;
        bool ownerInPvP = false;
        bool ownerInSafeZone = false;
        if (owner)
        {
            if (owner.GetIdentity())
                ownerName = owner.GetIdentity().GetName();
            ownerInPvE = owner.netSync_IsInPvEZone;
            ownerInPvP = owner.netSync_IsInPvPZone;
            ownerInSafeZone = owner.netSync_IsInSafeZone;
        }
        
        // Get damage amount
        float damage = 0.0;
        if (damageResult)
        {
            damage = damageResult.GetDamage("GlobalHealth", "Health");
        }
        
        // Get source type
        string sourceType = "Unknown";
        if (source)
            sourceType = source.GetType();
        
        // Debug: Log that override callback was called
        Print("[NinjinsCallBackTest] ===== ITEM DAMAGE OVERRIDE =====");
        Print("[NinjinsCallBackTest] Item: " + itemType);
        Print("[NinjinsCallBackTest] Attacker: " + attackerName);
        Print("[NinjinsCallBackTest] Owner: " + ownerName);
        Print("[NinjinsCallBackTest] Owner in PvE: " + ownerInPvE);
        Print("[NinjinsCallBackTest] Owner in PvP: " + ownerInPvP);
        Print("[NinjinsCallBackTest] Owner in SafeZone: " + ownerInSafeZone);
        Print("[NinjinsCallBackTest] Damage: " + damage);
        Print("[NinjinsCallBackTest] Source: " + sourceType);
        Print("[NinjinsCallBackTest] Ammo: " + ammo);
        Print("[NinjinsCallBackTest] Damage Zone: " + dmgZone);
        Print("[NinjinsCallBackTest] ShouldAllowItemDamage Result: " + shouldAllowItemDamageResult);
        Print("[NinjinsCallBackTest] ================================");
        
        // Check if damage was blocked and owner is in PvE zone
        // If so, override to allow damage even in PvE
        if (!shouldAllowItemDamageResult && ownerInPvE)
        {
            Print("[NinjinsCallBackTest] ===== OVERRIDING ITEM PvE PROTECTION =====");
            Print("[NinjinsCallBackTest] Item: " + itemType);
            Print("[NinjinsCallBackTest] Owner: " + ownerName + " (in PvE zone)");
            Print("[NinjinsCallBackTest] Attacker: " + attackerName);
            Print("[NinjinsCallBackTest] ALLOWING item damage despite PvE protection");
            Print("[NinjinsCallBackTest] ==========================================");
            
            // Force allow item damage even in PvE zone
            return true;
        }
        
        // For all other cases, respect ShouldAllowItemDamage's decision
        return shouldAllowItemDamageResult;
    }
}

File: NinjinsCallBackTestMod/scripts/5_Mission/MissionServer.c

modded class MissionServer extends MissionBase
{
	private ref NinjinsCallBackTest m_NinjinsCallBackTest;
	
	void ~MissionServer() {
	}

	void MissionServer()
	{
		Print("NinjinsCallBackTestMod mod has started !");
	}
	
	override void OnInit()
	{
		super.OnInit();
		
		// Register damage callback for logging damage events
		m_NinjinsCallBackTest = new NinjinsCallBackTest();
		DamageUtils.RegisterDamageCallback(m_NinjinsCallBackTest);
		
		Print("[NinjinsCallBackTestMod] Damage callback registered successfully.");
	}
};

Key Points:

  1. Override Behavior: The callback runs after ShouldAllowDamage makes its decision. You can override the decision by returning true to force allow damage, or return the original shouldAllowDamageResult to respect it.
  2. Player Damage: OnShouldAllowDamageOverride handles damage to players. Check victim.netSync_IsInPvEZone to determine if the victim is in a PvE zone.
  3. Item Damage: OnShouldAllowItemDamageOverride handles damage to items. Get the item owner with item.GetOwnerPlayer() and check their zone state.
  4. Zone State Checks: Use player.netSync_IsInPvEZone, player.netSync_IsInPvPZone, and player.netSync_IsInSafeZone to check zone states.
  5. Registration: Register the callback using DamageUtils.RegisterDamageCallback() in MissionServer.OnInit().
  6. Use Cases: Useful for special events, admin tools, custom PvP mechanics, or testing scenarios where you need to bypass zone protection.