FiveM Türschlosssystem: Türen und Schlüssel absichern
Füge jedem FiveM-Server Türschlösser hinzu. Mit Konfiguration, Schlüsseln, jobgebundenen Türen und Scripts für Polizei, Häuser und Geschäfte.
Agency Scripts
Gründer und leitender Entwickler bei Agency Scripts
Warum Türschlösser im Roleplay wichtig sind
Türschlosssysteme gehören zu den am meisten unterschätzten und zugleich unverzichtbaren Bestandteilen eines ernsthaften FiveM-Roleplay-Servers. Ohne eine ordentliche Türverwaltung können Spieler ungehindert Polizeiwachen, Personalräume in Krankenhäusern und Gangverstecke betreten, was die Immersion komplett zerstört. Mit einem gut umgesetzten Türschlosssystem können Serverbetreiber den Zugang zu jedem Innenraum auf der Karte kontrollieren, von Regierungsgebäuden bis hin zu Geschäften im Besitz von Spielern. Es schafft natürliche Grenzen, die Roleplay-Interaktionen wie den Austausch von Schlüsseln, Lockpicking-Szenarien und koordinierte Durchsuchungen ermöglichen. In diesem Guide zeigen wir dir, wie du mit ox_doorlock als Grundlage ein vollständiges Türschlosssystem aufbaust und es anschließend um eigene Lockpicking-Mechaniken, Keycard-Authentifizierung, Türgruppen und geschäftsspezifische Zugangskontrollen erweiterst.
ox_doorlock einrichten
Die Resource ox_doorlock ist die am weitesten verbreitete Lösung für Türschlösser im FiveM-Ökosystem. Sie bietet eine übersichtliche API, eine integrierte Admin-Oberfläche zum Platzieren von Türen und Unterstützung für Doppeltüren sowie animierte Türen im Garagenstil. Bevor du eigenen Code schreibst, brauchst du ein solides Fundament. Installiere ox_doorlock zusammen mit ox_lib, von dem die Resource für UI-Komponenten und Hilfsfunktionen abhängig ist. Deine server.cfg muss sicherstellen, dass die Resources in der richtigen Reihenfolge gestartet werden: zuerst die Datenbank, dann ox_lib und schließlich ox_doorlock. Sobald alles läuft, kannst du mit dem integrierten Admin-Befehl zu jeder Tür in der Spielwelt gehen und sie mit einem Klick registrieren.
-- server.cfg load order
ensure oxmysql
ensure ox_lib
ensure ox_doorlock
-- Grant admin access for door placement
add_ace group.admin command.doorlock allow
Nachdem du Türen über die Admin-Oberfläche registriert hast, speichert ox_doorlock jeden Eintrag mit Koordinaten, Ausrichtung, Model-Hash und Sperrstatus in der Datenbank. Diese Einträge kannst du programmgesteuert abfragen und bearbeiten. Die Resource stellt sowohl Exports als auch Events bereit, um Schlösser umzuschalten, den Status zu prüfen und den Zugang zu verwalten. Bevor du darauf eigene Funktionen aufbaust, musst du die Datenstruktur verstehen.
-- Checking if a specific door is locked
local doorId = 1
local isLocked = exports.ox_doorlock:getDoorState(doorId)
-- Toggle a door lock from server side
exports.ox_doorlock:setDoorState(doorId, not isLocked)
-- Listen for door state changes
AddEventHandler('ox_doorlock:stateChanged', function(id, state, source)
print(('Door %d changed to %s by player %s'):format(id, state and 'locked' or 'unlocked', source))
end)
Eine Lockpicking-Mechanik entwickeln
Lockpicking erweitert das Gameplay um eine Ebene, durch die verschlossene Türen für kriminelle Charaktere relevant werden. Türen sind dann keine absoluten Barrieren mehr, sondern Herausforderungen, bei denen es auf Geschick ankommt. Am besten eignet sich ein Minigame, das Timing und Präzision erfordert, damit sich der Erfolg verdient und nicht zufällig anfühlt. Wir verwenden das integrierte Skillcheck-System von ox_lib, um einen Lockpicking-Ablauf zu erstellen. Der Spieler muss dafür einen Dietrich im Inventar haben, eine Animation wird abgespielt und anschließend läuft ein mehrstufiger Skillcheck. Ein fehlgeschlagener Skillcheck sollte Konsequenzen haben, etwa dass der Dietrich zerbricht oder eine Dispatch-Nachricht die Polizei in der Nähe alarmiert.
-- client/lockpicking.lua
local function AttemptLockpick(doorId)
local hasLockpick = exports.ox_inventory:Search('count', 'lockpick')
if hasLockpick < 1 then
lib.notify({ title = 'No Lockpick', type = 'error' })
return
end
-- Play lockpicking animation
local ped = PlayerPedId()
TaskPlayAnim(ped, 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', 'machinic_loop_mechandlenry', 8.0, -8.0, -1, 1, 0, false, false, false)
-- Multi-stage skillcheck: easy, medium, hard
local success = lib.skillCheck({'easy', 'medium', 'hard'}, {'w', 'a', 's', 'd'})
ClearPedTasks(ped)
if success then
TriggerServerEvent('doorlock:lockpick:success', doorId)
lib.notify({ title = 'Lock Picked', description = 'The door clicks open.', type = 'success' })
else
TriggerServerEvent('doorlock:lockpick:fail', doorId)
lib.notify({ title = 'Failed', description = 'The lockpick snapped.', type = 'error' })
end
end
Auf der Serverseite musst du den Lockpicking-Versuch validieren, den Dietrich bei einem Fehlschlag entfernen, die Tür bei Erfolg entriegeln und optional eine Polizeialarmierung auslösen. Eine Ratenbegrenzung ist wichtig, um Spam-Versuche zu verhindern. Speichere pro Spieler und Tür eine Abklingzeit, damit der Spieler es nach einem Fehlschlag nicht sofort erneut versuchen kann. Der Server-Handler sollte außerdem prüfen, ob sich der Spieler tatsächlich in der Nähe der Tür befindet, die er knacken will, damit Remote-Exploits verhindert werden.
-- server/lockpicking.lua
local cooldowns = {}
RegisterNetEvent('doorlock:lockpick:success', function(doorId)
local src = source
local playerCoords = GetEntityCoords(GetPlayerPed(src))
-- Verify proximity to door (anti-cheat)
local doorData = exports.ox_doorlock:getDoor(doorId)
if not doorData then return end
if #(playerCoords - doorData.coords) > 3.0 then return end
-- Check cooldown
local key = ('%s:%s'):format(src, doorId)
if cooldowns[key] and os.time() - cooldowns[key] < 30 then return end
exports.ox_doorlock:setDoorState(doorId, 0) -- Unlock
cooldowns[key] = os.time()
-- Auto-relock after 60 seconds
SetTimeout(60000, function()
exports.ox_doorlock:setDoorState(doorId, 1)
end)
end)
RegisterNetEvent('doorlock:lockpick:fail', function(doorId)
local src = source
exports.ox_inventory:RemoveItem(src, 'lockpick', 1)
-- Alert police dispatch
TriggerEvent('dispatch:alert', {
coords = GetEntityCoords(GetPlayerPed(src)),
message = 'Attempted break-in reported',
code = '10-31',
job = 'police'
})
end)
Ein Keycard-System implementieren
Keycards bieten im Vergleich zu einfachen Schlüsseln eine strukturiertere Zugangskontrolle. Sie eignen sich gut für Bürogebäude, Regierungseinrichtungen und Sperrbereiche, in denen der Zugang an bestimmte Sicherheitsstufen gebunden sein soll. Das Keycard-System weist jeder Tür eine Sicherheitsstufe von 1 bis 5 zu. Spieler müssen eine Keycard mit einer gleich hohen oder höheren Sicherheitsstufe besitzen, um die Tür zu entriegeln. So entsteht eine natürliche Hierarchie: Eine Keycard der Stufe 3 öffnet alle Türen der Stufen 1, 2 und 3, aber nicht die Sperrbereiche der Stufen 4 und 5. Keycards können über Job-Systeme ausgegeben, als Beute gefunden oder im Rahmen eines Fortschrittssystems hergestellt werden.
-- shared/config.lua
Config = {}
Config.KeycardLevels = {
{ name = 'keycard_1', label = 'Green Keycard', level = 1 },
{ name = 'keycard_2', label = 'Blue Keycard', level = 2 },
{ name = 'keycard_3', label = 'Yellow Keycard', level = 3 },
{ name = 'keycard_4', label = 'Red Keycard', level = 4 },
{ name = 'keycard_5', label = 'Black Keycard', level = 5 },
}
Config.DoorSecurity = {
[10] = { level = 1, name = 'Office Lobby' },
[11] = { level = 2, name = 'Server Room' },
[12] = { level = 3, name = 'Executive Floor' },
[13] = { level = 4, name = 'Vault Anteroom' },
[14] = { level = 5, name = 'Main Vault' },
}
-- client/keycard.lua
local function TryKeycardAccess(doorId)
local security = Config.DoorSecurity[doorId]
if not security then return false end
for _, card in ipairs(Config.KeycardLevels) do
if card.level >= security.level then
local count = exports.ox_inventory:Search('count', card.name)
if count > 0 then
-- Play card swipe animation
lib.requestAnimDict('anim@heists@keycard@')
TaskPlayAnim(PlayerPedId(), 'anim@heists@keycard@', 'exit', 5.0, 1.0, -1, 16, 0, 0, 0, 0)
Wait(1200)
ClearPedTasks(PlayerPedId())
return true, card
end
end
end
return false
end
Türgruppen und Zugang zu Geschäften
Die Verwaltung einzelner Türen ist bei Hunderten verschlossenen Türen auf deinem Server nicht mehr skalierbar. Mit Türgruppen kannst du mehrere Türen einer benannten Gruppe zuordnen und den Zugang für die gesamte Gruppe gemeinsam verwalten. In einer Polizeiwache könnten 15 verschlossene Türen sein, die alle für den Polizeijob zugänglich sein sollen. Statt jede Tür einzeln zu konfigurieren, ordnest du sie alle der Gruppe "police_station" zu und vergibst den Zugang anhand des Job-Namens. Wenn ein Admin einen neuen Beamten einstellt, erhält dieser automatisch Zugang zu allen Türen der Gruppe, ganz ohne manuelle Konfiguration.
-- server/door_groups.lua
local DoorGroups = {
police_station = {
doors = {1, 2, 3, 4, 5, 6, 7, 8},
access = {
{ type = 'job', name = 'police', minGrade = 0 },
{ type = 'job', name = 'sheriff', minGrade = 0 },
}
},
pillbox_hospital = {
doors = {20, 21, 22, 23, 24},
access = {
{ type = 'job', name = 'ambulance', minGrade = 0 },
{ type = 'job', name = 'doctor', minGrade = 2 },
}
},
vangelico = {
doors = {30, 31},
access = {
{ type = 'job', name = 'jeweler', minGrade = 0 },
{ type = 'item', name = 'vangelico_key' },
}
},
}
function HasGroupAccess(source, groupName)
local group = DoorGroups[groupName]
if not group then return false end
for _, rule in ipairs(group.access) do
if rule.type == 'job' then
local job = GetPlayerJob(source)
if job and job.name == rule.name and job.grade >= (rule.minGrade or 0) then
return true
end
elseif rule.type == 'item' then
local count = exports.ox_inventory:GetItem(source, rule.name, nil, true)
if count and count > 0 then return true end
end
end
return false
end
Türen von Geschäften bieten eine weitere Möglichkeit, indem der Zugang an Besitzdaten geknüpft wird. Wenn ein Spieler ein Geschäft kauft, sollte er automatisch die Kontrolle über alle Türen erhalten, die zu diesem Geschäft gehören. Das System muss den Besitz in Echtzeit prüfen, da Immobilien verkauft oder übertragen werden können. Implementiere einen Callback, der deine Geschäftstabelle abfragt, um den aktuellen Besitzer zu ermitteln. Speichere das Ergebnis anschließend für kurze Zeit im Cache, damit die Datenbank nicht bei jeder Türinteraktion abgefragt wird.
Türstatus zwischen Clients synchronisieren
Die Synchronisierung von Türen gehört zu den anspruchsvolleren Aspekten eines Türschlosssystems. Wenn ein Spieler eine Tür entriegelt, muss jeder Spieler in der Nähe sehen, wie sie aufgeht. Die nativen Türsteuerungen von FiveM funktionieren pro Client, das heißt, jeder Client verwaltet den Türstatus unabhängig. Ohne korrekte Synchronisierung sieht ein Spieler eine offene Tür, während sie für einen anderen geschlossen ist. ox_doorlock übernimmt die grundlegende Statussynchronisierung. Bei eigenen Erweiterungen musst du jedoch auf die Weitergabe des Status achten. Verwende State Bags oder serverseitig autorisierte Events, um Türänderungen an alle Spieler in einem angemessenen Umkreis zu übertragen. Synchronisiere nicht jede Türänderung mit dem gesamten Server, denn auf großen Servern mit Hunderten von Türen entsteht dadurch unnötige Netzwerklast.
-- server: broadcast door state to nearby players only
local function SyncDoorToNearby(doorId, state, coords, range)
range = range or 100.0
local players = GetPlayers()
for _, playerId in ipairs(players) do
local ped = GetPlayerPed(playerId)
if ped and DoesEntityExist(ped) then
local playerCoords = GetEntityCoords(ped)
if #(playerCoords - coords) <= range then
TriggerClientEvent('doorlock:sync', tonumber(playerId), doorId, state)
end
end
end
end
-- client: apply synced door state
RegisterNetEvent('doorlock:sync', function(doorId, state)
local door = exports.ox_doorlock:getDoor(doorId)
if door then
door.state = state
end
end)
Sicherheit und Schutz vor Exploits
Türschlosssysteme sind ein häufiges Ziel von Exploitern, da das Umgehen einer Tür Zugang zu gesperrten Gegenständen, Waffen oder Geld verschaffen kann. Jede Anfrage zum Ändern des Türstatus muss serverseitig validiert werden. Vertraue niemals vom Client gesendeten Tür-IDs, ohne die Nähe des Spielers und dessen Zugangsrechte zu prüfen. Protokolliere jede Änderung des Türstatus, damit Admins verdächtige Muster untersuchen können, zum Beispiel wenn ein Spieler Tresortüren entriegelt, zu denen er keinen Zugang haben sollte. Begrenze die Häufigkeit aller Türinteraktionen, um Brute-Force-Versuche beim Lockpicking-System zu verhindern. Ergänze einen serverseitigen Event-Logger, der die Steam-ID des Spielers, die Tür-ID, den Zeitstempel und die Zugangsmethode für Prüfprotokolle speichert. Solche Logs sind bei der Untersuchung von Exploits oder bei Streitigkeiten zwischen Spielern darüber, wer wann worauf zugegriffen hat, äußerst hilfreich.
Alles zusammenführen
Ein produktionsreifes Türschlosssystem vereint all diese Komponenten in einem einheitlichen Framework. Als Einstiegspunkt dient eine Interaktion über ein Target-System, entweder ox_target oder qb-target. Dieses erkennt, wenn ein Spieler eine registrierte Tür anschaut, und zeigt je nach Zugangsstufe passende Optionen an. Hat der Spieler Job-Zugang, wird eine Schaltfläche zum Entriegeln angezeigt. Besitzt er eine Keycard, wird eine Option zum Durchziehen der Karte angezeigt. Hat er einen Dietrich und ist die Tür zum Knacken freigegeben, wird die entsprechende Option angezeigt. Jede dieser Aktionen wird an den passenden Handler weitergegeben, der sie auf dem Server validiert und das Ergebnis mit Clients in der Nähe synchronisiert. Die Konfiguration sollte datenbasiert über Datenbankeinträge oder gemeinsam genutzte Config-Dateien erfolgen, damit Serverbetreiber Türzugänge hinzufügen, entfernen und ändern können, ohne den Code anzufassen. Dank dieser modularen Architektur kannst du das Lockpicking-Minigame austauschen, neue Zugangsmethoden wie biometrische Scanner hinzufügen oder ganz andere Inventarsysteme integrieren, ohne die Kernlogik neu zu schreiben.