Aktionen
Aktionen sind Code-Snippets, die ausgeführt werden, wenn ein Log-Ereignis vom Spielserver importiert wird. Verwende sie, um Daten zu verarbeiten und Reaktionen zu automatisieren.
Aktionen unterstützen jetzt sowohl einen server-spezifischen Workflow als auch einen Marktplatz-Workflow. Für das gemeinsame Modell hinter Marktplatz-, installierten und eigenen Aktionen siehe das Konzept Aktions-Marktplatz.
Hinweis zur Datenquelle: Aktionen hängen von importierten Server-Logs ab. Kommen Logs später an, wird die Aktionsausführung entsprechend verzögert. Vollständige Informationen zum Import-Verhalten findest du im Konzept des Log-Imports.
Navigationspfad
Server Admin->Aktionen
Tabs
Die Aktionsansicht ist in drei Tabs aufgeteilt:
Marktplatz: öffentliche Creator-Aktionen durchsuchen und auf dem aktuellen Server installierenInstalliert: bereits auf diesem Server installierte Marktplatz-Aktionen verwaltenEigene Aktionen: servereigene Aktionen mit lokalem Lua-Code erstellen und pflegen
Installierte Marktplatz-Aktionen übernehmen Code, Trigger-Ereignis und Konfigurationsschema aus der Creator-Definition. Server-Mitarbeiter verwalten nur den Aktiv-Status und die Konfigurationswerte für diesen Server.
Zugriffsvoraussetzungen
Aktionen haben jetzt drei Berechtigungsebenen:
- actions.edit: Erlaubt das Bearbeiten serverseitiger Konfigurationswerte sowie das Aktivieren und Deaktivieren installierter oder eigener Aktionen
- actions.manage_marketplace: Erlaubt das Installieren von Marktplatz-Aktionen sowie das Entfernen installierter Marktplatz-Aktionen
- actions.edit_code: Erlaubt das Erstellen und Pflegen eigener Aktionen einschließlich Lua-Code und Konfigurationsschema
Diese Trennung ermöglicht es Serveradministratoren, operative Aktionsverwaltung, Marktplatz-Verwaltung und lokale Code-Erstellung getrennt zu vergeben.
Aktionssprache: Lua
Aktionen werden in Lua geschrieben, einer leichtgewichtigen, hochrangigen, multi-paradigmatischen Programmiersprache, die primär für den eingebetteten Einsatz in Anwendungen entwickelt wurde.
Aktionsskripte laufen direkt. Zwei globale Variablen sind verfügbar:
data: Ereignis-Payload des aktuellen Log-Ereignissesconfig: Aufgelöste Aktionskonfigurationswerte
Um Werte beim Erstellen von Skripten zu prüfen, verwende die print-Funktion.
Hier ist ein Beispiel einer einfachen Aktion, die die Log-Ereignisdaten in die Konsole ausgibt:
print(data);
Konfigurationsfelder
Aktionen unterstützen benutzerdefinierte Konfigurationsfelder, mit denen du deine Lua-Skripte parametrisieren kannst, ohne den Code zu bearbeiten. Das ist nützlich für:
- Moderatoren das Anpassen von Aktionsverhalten zu erlauben, ohne Code-Zugriff
- Dieselbe Aktionsvorlage mit verschiedenen Einstellungen wiederzuverwenden
- Sensible Werte (wie Kanal-IDs) vom Code zu trennen
Bei Marktplatz-Aktionen werden diese Felder vom Creator definiert. Server-Admins setzen nur die Werte in der installierten Server-Kopie.
Konfigurationsfelder definieren
Bei eigenen Aktionen werden Konfigurationsfelder im Code-Tab über den Konfigurationsschema-Builder definiert.
Bei Marktplatz-Aktionen werden Konfigurationsfelder im Creator Space vom Autor definiert und erscheinen anschließend auf jeder Server-Installation.
Jedes Feld hat:
- Name: Schlüssel in Lua (
config.<name>) - Typ:
string,numberoderboolean - Standard: Fallback-Wert, wenn kein Wert in den Einstellungen gespeichert ist
- Beschreibung: Hilfetext, der im Einstellungs-Tab angezeigt wird
Empfohlene Benennung: camelCase oder snake_case ohne Leerzeichen.
Konfigurationswerte setzen
Nachdem ein Feld im Code-Tab definiert wurde, können Benutzer mit actions.edit dessen Wert im Einstellungs-Tab setzen.
Zur Laufzeit werden Config-Werte wie folgt aufgelöst:
- Gespeicherter Wert aus dem Einstellungs-Tab (falls vorhanden)
- Andernfalls der Feld-Standard aus dem Schema
- Andernfalls
nil
Nur im Schema definierte Felder werden in config verfügbar gemacht.
Typverhalten
Config-Werte werden vor der Skriptausführung nach Feldtyp umgewandelt:
string: Wert wird als Zeichenkette verwendetnumber: Als Integer oder Float geparstboolean: Als true/false geparst
Leere Werte werden als nil behandelt.
Neben den Basistypen gibt es Auswahltypen: discord_channel_select, discord_role_select, discord_category_select, discord_guild_select, player_select, faction_select und generic_select. Wähle den Typ danach, was das Feld bezeichnet, nicht nach dem Lua-Wert: Ein Discord-Kanal, eine Rolle, eine Kategorie oder eine Gilde → der passende discord_*_select; ein Spieler → player_select; eine Fraktion → faction_select; eine feste Auswahl → generic_select. Der Wert einer Auswahl ist bereits die ID (bzw. der Benutzername) als Zeichenkette, die die Action-Funktionen erwarten — ein reines string-Feld wäre dagegen ein fehleranfälliges Freitextfeld. Verwende string nur für echten Freitext.
Konfiguration in Lua verwenden
Konfigurationswerte sind über die config-Tabelle verfügbar:
-- Konfigurationswerte abrufen
discord.send_to_channel(config.channelId, "Spieler getötet: " .. data.victim.username);
if config.notifyOnHeadshot and data.weapon == "Mosin" then
discord.send_to_channel(config.channelId, "Mosin-Kill erkannt");
end
if data.distance > config.minDistanceForBonus then
player.change_balance(data.killer.username, config.bonusAmount, "Weitschuss-Bonus");
end
Beispielkonfiguration
Für eine Kill-Benachrichtigungsaktion könntest du folgendes definieren:
| Name | Typ | Standard | Beschreibung |
|---|---|---|---|
| channelId | discord_channel_select | Discord-Kanal für Benachrichtigungen | |
| notifyOnHeadshot | boolean | true | Zusätzliche Nachricht bei Kopfschüssen |
| minDistanceForBonus | number | 200 | Mindestdistanz für Bonusbelohnung |
| bonusAmount | number | 50 | Währungsbonus für Weitschuss-Kills |
Moderatoren können diese Werte dann im Einstellungs-Tab anpassen, ohne Code-Zugriff zu benötigen.
Funktionen
Um die Aktion bei einem neuen Log-Eintrag anzupassen, bietet der Code-Editor durchsuchbare Autovervollständigung mit [STRG] + [LEERTASTE].
Folgende Funktionen stehen in Aktionen zur Verfügung:
Debug-Ausgabe
Gibt die Daten in die Konsole aus.
print({name = "John", age = 30}); -- wird als JSON ausgegeben
print("Hallo, Welt!");
Server-Namespace
Der Server-Namespace enthält alle serverbezogenen Funktionen.
server.name()
Gibt den Namen des Servers zurück.
print(server.name);
server.current_time()
Gibt die aktuelle Serverzeit zurück.
print(server.current_time);
server.ban_player(username: string, reason: string = "", length: string = "99 years")
Erstellt eine Bann-Einschränkung für den Spieler mit dem angegebenen Benutzernamen. Der Bann ist unter Admin → Einschränkungen sichtbar und verwaltbar.
server.ban_player("John");
server.ban_player("John", "Cheaten");
server.ban_player("John", "Cheaten", "1 day");
server.currency()
Gibt das Währungssymbol des Servers zurück, wie es unter Einstellungen → Wirtschaft konfiguriert ist.
print(server.currency); -- z.B. "Coins"
discord.send_to_channel(config.channelId, "Du hast 100 " .. server.currency .. " erhalten!");
Discord-Namespace
Der Discord-Namespace enthält alle Discord-bezogenen Funktionen.
Achtung: Alle Discord-IDs müssen als Zeichenketten (in Anführungszeichen) angegeben werden, da die Programmiersprache sie sonst als Zahlen interpretiert und mathematisch darstellt, was zu Fehlern führt.
discord.send_to_player(username: string, content: string)
Sendet eine Nachricht an den Spieler mit dem angegebenen Benutzernamen.
discord.send_to_player("John", "Hallo, John!");
discord.send_to_player("h3eh23ieuh3ei2he3", "Hallo, John!"); -- wirft eine "Spieler nicht gefunden"-Exception
discord.send_to_channel(channel: string, content: string)
Sendet eine Nachricht an den Kanal mit der angegebenen ID.
discord.send_to_channel("1234567890", "Hallo, Welt!");
discord.mention(username: string)
Gibt den Erwähnungs-String für den Spieler mit dem angegebenen Benutzernamen zurück. Kann in Discord-Nachrichten verwendet werden, um auf den Discord-Account zu verweisen. Wird kein Spieler gefunden, wird der Benutzername ohne Formatierung zurückgegeben.
print(discord.mention("John")); -- gibt "<@1234567890>" zurück
print(discord.mention("h3eh23ieuh3ei2he3")); -- gibt "h3eh23ieuh3ei" zurück
Player-Namespace
Der Player-Namespace enthält alle spielerbezogenen Funktionen.
player.change_balance(username: string, amount: number, description: string = "")
Ändert das Guthaben des Spielers mit dem angegebenen Benutzernamen.
player.change_balance("John", 100);
player.change_balance("John", 100, "Guter Schuss!");
player.change_balance("John", -100, "Schlechter Schuss!"); -- negativer Betrag zum Abziehen
player.get_balance(username: string)
Gibt das aktuelle Guthaben des Spielers mit dem angegebenen Benutzernamen zurück, oder nil, wenn kein passender Spieler gefunden wird.
local balance = player.get_balance("John");
if balance ~= nil then
print("Guthaben: " .. balance);
end
player.statistics(username: string)
Gibt ein Statistik-Objekt für den Spieler mit dem angegebenen Benutzernamen zurück. Gibt nil zurück, wenn kein passender Spieler gefunden wird.
local stats = player.statistics("John");
print(stats.online_streak); -- gibt die aktuelle Online-Tages-Serie aus
Rückgabefelder:
| Feld | Typ | Beschreibung |
|---|---|---|
pvp_kills | int | Gesamte PvP-Kills |
last_pvp_kill | string|nil | Zeitstempel des letzten Kills |
pvp_deaths | int | Gesamte PvP-Tode |
last_pvp_death | string|nil | Zeitstempel des letzten PvP-Todes |
pvp_kd | float | Kill/Death-Verhältnis |
deaths | int | Gesamte Tode (alle Ursachen) |
kill_streak | int | Kills seit dem letzten Tod |
death_streak | int | Tode seit dem letzten Kill |
longest_kill | float | Größte Kill-Distanz in Metern |
online_streak | int | Aufeinanderfolgende Kalendertage mit mindestens einer Sitzung (einschließlich heute) |
Beispielausgabe:
{
"pvp_kills": 8,
"last_pvp_kill": "2024-06-19 22:32:23",
"pvp_deaths": 23,
"last_pvp_death": "2024-06-19 22:49:19",
"pvp_kd": 0.35,
"deaths": 58,
"kill_streak": 0,
"death_streak": 2,
"longest_kill": 44.78,
"online_streak": 3
}
Beispiel: Online-Streak-Belohnung
Einen Bonus vergeben, der mit der Anzahl aufeinanderfolgender Online-Tage skaliert:
local stats = player.statistics(data.player.username);
if stats.online_streak >= 3 then
local bonus = config.base_reward + (stats.online_streak * config.streak_bonus);
player.change_balance(data.player.username, bonus, "Online-Streak: " .. stats.online_streak .. " Tage");
else
player.change_balance(data.player.username, config.base_reward, "Tägliche Belohnung");
end
player.get_faction(username: string)
Gibt ein Fraktions-Datenobjekt für die aktuelle Fraktion des Spielers zurück, oder nil, wenn der Spieler nicht gefunden wurde oder in keiner Fraktion ist.
local faction = player.get_faction("John");
if faction then
print(faction.id); -- z.B. 42
print(faction.name); -- z.B. "Wolves"
end
Rückgabefelder:
| Feld | Typ | Beschreibung |
|---|---|---|
id | int | Fraktions-ID |
name | string | Fraktionsname |
color | string|nil | Fraktionsfarbe als Hex-Code |
image | string|nil | Fraktionsbild-Pfad |
balance | int|nil | Aktuelles Fraktionskassen-Guthaben |
discord_role_id | string|nil | Verknüpfte Discord-Rollen-ID |
Beispielausgabe:
{
"id": 42,
"name": "Wolves",
"color": "#ff0000",
"image": null,
"balance": 1500,
"discord_role_id": "1234567890"
}
Beispiel: Gesamte Fraktion bei einem Kill belohnen
local faction = player.get_faction(data.killer.username);
if faction then
faction.change_balance(faction.id, config.faction_reward, "Kill-Belohnung");
discord.send_to_channel(config.channel, faction.name .. " hat " .. config.faction_reward .. " für einen Kill erhalten!");
end
player.get_faction hat früher die Fraktions-ID als einfachen String zurückgegeben. Es wird jetzt ein Objekt zurückgegeben — verwende .id, um auf die ID zuzugreifen.
player.get_name(username: string)
Löst den Anzeigenamen des Spielers auf. Wenn kein Name für den Benutzer gesetzt ist oder kein passender Benutzer gefunden wird, wird der eingegebene Benutzername zurückgegeben.
player.get_name("johnnyboy420"); -- gibt den Anzeigenamen des Spielers zurück: "John Doe"
player.get_name("nouserwiththatusername"); -- Benutzer nicht gefunden, gibt die Eingabe zurück: "nouserwiththatusername"
player.get_main_account(username: string)
Ermittelt den Haupt-Account-Benutzernamen zu einem Spieler. Geht alle Spieler durch, die mit dem angegebenen Spieler mindestens ein Gerät teilen, und liefert den Benutzernamen des ersten Spielers zurück, dem ein Benutzer-Account zugeordnet ist. Hat der Spieler selbst bereits einen verknüpften Benutzer-Account, wird dessen eigener Benutzername zurückgegeben. Kann kein verknüpfter Account gefunden werden oder ist der Spieler unbekannt, wird der eingegebene Benutzername unverändert zurückgegeben.
player.get_main_account("alt_account"); -- gibt "main_account" zurück, falls Alt und Main ein Gerät teilen
player.get_main_account("solo_player"); -- gibt "solo_player" zurück, wenn kein Haupt-Account verknüpft ist
Beispiel: Belohnungen dem Haupt-Account gutschreiben
local main = player.get_main_account(data.killer.username);
player.change_balance(main, config.reward or 50, "Kill-Belohnung (über " .. data.killer.username .. ")");
player.has_discord_role(username: string, role: string)
Prüft, ob der Spieler mit dem angegebenen Benutzernamen die angegebene Discord-Rolle hat. Akzeptiert sowohl den Rollennamen als auch die Rollen-ID.
player.has_discord_role("John", "6553535632356535564"); -- funktioniert mit Rollen-IDs
player.has_discord_role("John", "Admin"); -- funktioniert auch mit Rollennamen
player.ensure_discord_role(username: string, roleId: string)
Weist dem Spieler die angegebene Discord-Rolle zu. Falls der Spieler die Rolle bereits hat, passiert nichts. Die Rolle muss über ihre Discord-Rollen-ID angegeben werden.
player.ensure_discord_role("John", "1234567890123456789");
player.remove_discord_role(username: string, roleId: string)
Entfernt die angegebene Discord-Rolle vom Spieler. Falls der Spieler die Rolle nicht hat, passiert nichts. Die Rolle muss über ihre Discord-Rollen-ID angegeben werden.
player.remove_discord_role("John", "1234567890123456789");
player.list(filters?: table)
Gibt eine Liste aller Spieler auf dem Server zurück, optional gefiltert. Ohne Argumente werden alle Spieler zurückgegeben.
local players = player.list();
local vip_spieler = player.list({ group = "vip" });
local aktive_mit_discord = player.list({
has_discord = true,
last_online_after = "-7d",
});
Verfügbare Filter (alle optional):
| Filter | Typ | Beschreibung |
|---|---|---|
group | string | Nur Spieler, die Mitglied dieser Gruppe sind (nach Name) |
whitelisted | bool | Nur auf der Whitelist stehende / nicht stehende Spieler |
banned | bool | Nur Spieler mit aktiver Bann-Einschränkung / Spieler ohne aktive Bann-Einschränkung |
has_discord | bool | Nur Spieler mit verknüpftem Discord-Konto |
has_balance_above | number | Nur Spieler, deren Guthaben über diesem Wert liegt |
has_balance_below | number | Nur Spieler, deren Guthaben unter diesem Wert liegt |
last_online_after | string | Nur Spieler, die nach diesem Zeitpunkt zuletzt gesehen wurden. Unterstützt relative Formate wie "-7d", "-2h", "-1m", "-1y" oder einen absoluten Datums-String |
last_online_before | string | Nur Spieler, die vor diesem Zeitpunkt zuletzt gesehen wurden. Gleiches Format wie oben |
in_faction | bool oder string | true = in einer beliebigen Fraktion, false = in keiner Fraktion, oder Fraktionsname/-ID für eine bestimmte Fraktion |
variable | string oder table | Nur Spieler, bei denen eine Variable mit diesem Namen gesetzt ist. Als String wird nur der Name geprüft, als Tabelle { name = "...", value = "..." } zusätzlich der Wert |
Rückgabefelder (ein Eintrag pro passendem Spieler):
| Feld | Typ | Beschreibung |
|---|---|---|
id | int | Interne Spieler-ID |
username | string | Spieler-Benutzername |
balance | int|nil | Aktuelles Guthaben |
last_online | string|nil | Zuletzt-gesehen-Zeitstempel (Y-m-d H:i:s) |
whitelisted | bool | Ob der Spieler auf der Whitelist steht |
banned | bool | Ob der Spieler eine aktive Bann-Einschränkung hat |
Beispiel: Tägliche Guthabenbelohnung für alle VIP-Mitglieder
local players = player.list({
group = "vip",
last_online_after = "-30d",
});
for _, p in ipairs(players) do
player.change_balance(p.username, 100, "Monatlicher VIP-Bonus");
end
Beispiel: Inaktive Spieler via Discord benachrichtigen
local inaktive = player.list({
has_discord = true,
last_online_before = "-14d",
banned = false,
});
for _, p in ipairs(inaktive) do
discord.send_to_player(p.username, "Wir vermissen dich auf dem Server! Komm bald wieder.");
end
Beispiel: Nach Variablenwert filtern
-- Alle Spieler, deren Variable "vip_tier" auf "gold" gesetzt ist
local gold_vips = player.list({ variable = { name = "vip_tier", value = "gold" } });
for _, p in ipairs(gold_vips) do
player.change_balance(p.username, 500, "Gold-VIP Tagesbonus");
end
player.get_variable(username: string, name: string)
Gibt den Wert einer Spieler-Variablen zurück oder nil, wenn die Variable nicht gesetzt ist.
local result = player.get_variable("John", "kill_count");
print(result.value); -- gibt den gespeicherten Wert aus, oder nil
Rückgabefelder:
| Feld | Typ | Beschreibung |
|---|---|---|
name | string | Variablenname |
value | string|nil | Gespeicherter Wert, oder nil wenn nicht gesetzt |
player.set_variable(username: string, name: string, value: string|nil)
Erstellt oder aktualisiert eine Spieler-Variable. Mit nil als Wert wird die Variable gelöscht.
player.set_variable("John", "kill_count", "42");
player.set_variable("John", "vip_tier", "gold");
player.set_variable("John", "temp_flag", nil); -- löscht die Variable
Beispiel: Kill-Streaks mit Variablen verfolgen
local streak_result = player.get_variable(data.killer.username, "kill_streak");
local streak = tonumber(streak_result.value) or 0;
streak = streak + 1;
player.set_variable(data.killer.username, "kill_streak", tostring(streak));
if streak >= 5 then
player.change_balance(data.killer.username, 250, "Kill-Streak x" .. streak);
discord.send_to_channel(config.feedChannel, data.killer.username .. " hat einen " .. streak .. "-Kill-Streak!");
end
-- Streak des Opfers zurücksetzen
player.set_variable(data.victim.username, "kill_streak", "0");
player.last_position(username: string)
Gibt die letzte bekannte Ingame-Position des Spielers zurück, basierend auf seinem letzten Online-Ping (wird etwa alle 5 Minuten aufgezeichnet, während er online ist), oder nil, wenn der Spieler nicht gefunden wird oder keine Position aufgezeichnet wurde. Die Position kann Minuten oder Tage alt sein — prüfe das Feld time, wenn Aktualität wichtig ist.
local pos = player.last_position(data.target.username);
if pos then
discord.send_to_channel(config.channel, "Zuletzt gesehen bei " .. string.format("%.0f / %.0f", pos.x, pos.z));
end
Rückgabefelder:
| Feld | Typ | Beschreibung |
|---|---|---|
x | float | Ost/West-Weltkoordinate |
y | float | Höhe |
z | float | Nord/Süd-Weltkoordinate |
time | string | Zeitpunkt der Aufzeichnung (YYYY-MM-DD HH:MM:SS, UTC) |
Fraktions-Namespace
Der Fraktions-Namespace enthält alle fraktionsbezogenen Aktionsfunktionen.
faction.change_balance(id: string, amount: number, description: string = "")
Ändert das Guthaben der Fraktion mit der angegebenen ID.
faction.change_balance("123", 100);
faction.change_balance("123", 100, "Gute Leute!");
faction.change_balance("123", -100, "Regelverstoß!"); -- negativer Betrag zum Abziehen
Bounty-Namespace
Der Bounty-Namespace stellt die Kopfgelder des Hitman-Systems bereit.
bounty.list()
Gibt alle offenen Kopfgelder auf dem Server zurück, neueste zuerst. Abgeschlossene oder stornierte Kopfgelder sind nie enthalten. Gibt eine leere Liste zurück, wenn kein Kopfgeld offen ist — das Iterieren ist also immer sicher.
local bounties = bounty.list();
print("Offene Kopfgelder: " .. #bounties);
Rückgabefelder (ein Eintrag pro Kopfgeld):
| Feld | Typ | Beschreibung |
|---|---|---|
id | int | Interne Kopfgeld-ID |
target | string | Benutzername des Spielers, auf den das Kopfgeld ausgesetzt ist |
created_by | string | Benutzername des Spielers, der das Kopfgeld ausgesetzt hat |
amount | int | Kopfgeldbetrag |
reason | string|nil | Beim Aussetzen angegebener Grund |
target_all_accounts | bool | Ob der Kill eines beliebigen mit dem Gerät des Ziels verknüpften Accounts ausgezahlt wird |
created_at | string | Zeitpunkt des Aussetzens (YYYY-MM-DD HH:MM:SS, UTC) |
Auf dasselbe Ziel können mehrere Kopfgelder existieren — gruppiere selbst nach target, wenn du einen Eintrag pro Spieler brauchst.
Beispiel: Geplanter Kopfgeld-Statusbericht
Poste die letzte bekannte Position jedes Spielers mit offenem Kopfgeld, z. B. aus einer geplanten benutzerdefinierten Aktion. Wenn kein Kopfgeld offen ist, wird keine Nachricht gesendet:
local bounties = bounty.list();
if #bounties > 0 then
local lines = {};
for _, b in ipairs(bounties) do
local pos = player.last_position(b.target);
local where = pos and string.format("%.0f / %.0f", pos.x, pos.z) or "unbekannt";
table.insert(lines, b.target .. " (" .. b.amount .. ") — zuletzt gesehen: " .. where);
end
discord.send_to_channel(config.channel, "**Offene Kopfgelder:**\n" .. table.concat(lines, "\n"));
end
Math-Namespace
Der Math-Namespace enthält Hilfsfunktionen für Berechnungen.
math.distance(x1: number, z1: number, x2: number, z2: number)
Berechnet die 2D-Distanz zwischen zwei Punkten. Nützlich, um zu prüfen, ob eine Position innerhalb eines bestimmten Radius einer anderen Position liegt.
-- Distanz zwischen zwei Koordinaten berechnen
local dist = math.distance(1000, 2000, 1500, 2500);
print("Distanz: " .. dist); -- gibt ungefähr 707.1 aus
Beispiel: Radius-Prüfung
Prüfen, ob ein Kill innerhalb eines bestimmten Radius einer Location stattgefunden hat (z.B. einer Safezone oder Basis):
local safezone_x = 5000;
local safezone_z = 5000;
local safezone_radius = 100;
local kill_distance = math.distance(data.x, data.z, safezone_x, safezone_z);
if kill_distance < safezone_radius then
discord.send_to_channel(config.alertChannel, "Kill innerhalb der Safezone erkannt!");
server.ban_player(data.killer, "Kill in Safezone", "1 day");
end
Beispiel: Weitschuss-Kills belohnen
-- Distanz zwischen Killer und Opfer berechnen
local kill_range = math.distance(data.killerX, data.killerZ, data.victimX, data.victimZ);
if kill_range > 500 then
player.change_balance(data.killer, 100, "Weitschuss-Bonus (" .. math.floor(kill_range) .. "m)");
discord.send_to_channel(config.feedChannel, data.killer .. " hat einen " .. math.floor(kill_range) .. "m-Kill erzielt!");
end
HTTP-Namespace
Der HTTP-Namespace ermöglicht es Aktionen, ausgehende HTTP-Anfragen an externe Dienste zu stellen. Anfragen haben ein Timeout von 5 Sekunden.
http.get(url: string, headers?: table)
Führt eine HTTP-GET-Anfrage durch. Gibt zwei Werte zurück: den HTTP-Statuscode und den Antwort-Body als String.
local status, body = http.get("https://api.example.com/data");
print(status); -- z.B. 200
print(body); -- roher Antwort-Body als String
Mit benutzerdefinierten Headern:
local status, body = http.get("https://api.example.com/data", {
["Authorization"] = "Bearer meintoken",
["Accept"] = "application/json",
});
http.post(url: string, body?: string, headers?: table)
Führt eine HTTP-POST-Anfrage durch. Gibt zwei Werte zurück: den HTTP-Statuscode und den Antwort-Body als String.
local status, body = http.post("https://api.example.com/webhook", "payload=hallo");
JSON-Body senden:
local payload = json.encode({ event = "kill", player = data.killer.username });
local status, body = http.post("https://api.example.com/events", payload, {
["Content-Type"] = "application/json",
["Authorization"] = "Bearer meintoken",
});
Beispiel: Externen Webhook bei einem Kill-Event benachrichtigen
local payload = json.encode({
killer = data.killer.username,
victim = data.victim.username,
weapon = data.weapon,
});
local status, body = http.post(config.webhookUrl, payload, {
["Content-Type"] = "application/json",
});
if status ~= 200 then
print("Webhook fehlgeschlagen mit Status: " .. status);
end
JSON-Namespace
Der JSON-Namespace stellt JSON-Kodierung und -Dekodierung bereit, um mit API-Antworten und Payloads zu arbeiten.
json.encode(value)
Kodiert einen Lua-Wert (Tabelle, String, Zahl, Boolean) als JSON-String.
local encoded = json.encode({ name = "John", score = 42 });
print(encoded); -- {"name":"John","score":42}
json.decode(json: string)
Parst einen JSON-String und gibt eine Lua-Tabelle zurück.
local decoded = json.decode('{"name":"John","score":42}');
print(decoded.name); -- John
print(decoded.score); -- 42
Beispiel: JSON-API aufrufen und Antwort verarbeiten
local status, body = http.get("https://api.example.com/players/" .. data.player.username);
if status == 200 then
local result = json.decode(body);
if result.vip == true then
player.change_balance(data.player.username, config.vipBonus, "VIP-Bonus");
end
end
In-App-Events
Die meisten Action-Trigger stammen direkt aus den Spielserver-Logs (Kills, Verbindungen, Basisbau usw.). Eine kleine Anzahl von Events wird von dzbot selbst erzeugt, wenn etwas innerhalb der Plattform passiert. Diese synthetischen Events durchlaufen dieselbe Action-Pipeline wie Game-Log-Events.
bounty_placed
Wird ausgelöst, wenn ein Spieler über den Shop ein Kopfgeld auf einen anderen Spieler aussetzt. Verwende diesen Trigger, um das Ziel zu benachrichtigen, in einem Discord-Kanal zu posten oder eine andere Automatisierung zu starten, sobald ein Kopfgeld aktiv wird.
Event-Payload:
| Feld | Typ | Beschreibung |
|---|---|---|
data.target.username | string | Benutzername des Spielers, auf den das Kopfgeld ausgesetzt wurde |
data.target.identifier | string | Steam/DayZ-ID des Ziels |
data.placedBy.username | string | Benutzername des Spielers, der das Kopfgeld ausgesetzt hat |
data.placedBy.identifier | string | Steam/DayZ-ID des Auftraggebers |
data.amount | int | Höhe des Kopfgelds in der Server-Währung |
data.reason | string|nil | Optionale Begründung des Auftraggebers |
data.bountyId | int|nil | Interne Kopfgeld-ID |
Beispiel — DM an den Ziel-Spieler senden:
local reason = data.reason or "Kein Grund angegeben";
discord.send_to_player(
data.target.username,
"Ein Kopfgeld von **" .. data.amount .. "** wurde von "
.. data.placedBy.username .. " auf dich ausgesetzt.\n> " .. reason
);
Beispiel — Nachricht in einen Kanal senden:
local channelId = config.channel or "";
if channelId == "" then return end;
local reason = data.reason or "Kein Grund angegeben";
discord.send_to_channel(
channelId,
"**Kopfgeld ausgesetzt!** " .. discord.mention(data.placedBy.username)
.. " hat ein Kopfgeld von **" .. data.amount .. "** auf "
.. discord.mention(data.target.username) .. " ausgesetzt.\n> *" .. reason .. "*"
);