FiveM Radyo ve Dispatch Sistemi: PMA, pma-voice ve CAD'ler
FiveM telsiz, dispatch ve CAD sistemlerini kurun. pma-voice kanalları, MDT araçları ve polis, EMS ile mekanik ekipleri için en iyi script'ler.
Agency Scripts
Agency Scripts Kurucusu ve Baş Geliştiricisi
Radyo ve Dispatch Sistemi Mimarisi
Bir telsiz ve dispatch sistemi, bir FiveM roleplay sunucusundaki acil durum hizmetlerinin iletişim belkemiğidir. Polis, EMS ve itfaiye departmanlarının yapılandırılmış telsiz kanalları üzerinden müdahaleleri koordine etmesini, 911 çağrılarından ve otomatik tespit sistemlerinden gelen öncelikli dispatch bildirimlerini almasını ve GPS entegrasyonuyla birim konumlarını gerçek zamanlı takip etmesini sağlar. Mimari, birbirine bağlı üç sistemden oluşur: kimin hangi frekansta konuşabileceğini yöneten telsiz kanalı altyapısı, gelen çağrıları işleyip uygun birimlere yönlendiren dispatch kuyruğu ve birim konumlarını paylaşımlı bir haritada görselleştiren GPS takip katmanı. Basit sohbet tabanlı iletişimin aksine, düzgün bir telsiz sistemi frekans değiştirme, sadece kanala bağlıyken telsiz konuşmalarını duyma ve gerçek dünyadaki acil durum servislerini yansıtan yapılandırılmış iletişim protokollerini izleme atmosferini katar. Tüm sistem hileyi önlemek için sunucu taraflı state yönetimiyle çalışırken, istemci taraflı NUI telsiz arayüzünü ve dispatch panelini sunar.
Radyo Kanalı Sistemi
Radyo kanalları, farklı departman ve birimlerin ses karmaşası olmadan koordine olabilmesi için iletişimi organize eder. Her kanalın bir frekans numarası, mesleklere dayalı erişim kısıtlamaları ve o anda bağlı olan oyuncuların listesi bulunur. Sistem birden fazla kanal türünü destekler: genel iletişim için departman çapında kanallar, kovalama veya banka soygunu müdahalesi gibi belirli operasyonlar için taktiksel kanallar ve polis ile EMS'in ortak olaylarda koordine olabileceği departmanlar arası kanallar. Oyuncular bir radyo eşyası veya NUI arayüzü üzerinden bir kanala katılır ve o kanaldan iletilen tüm ses veya metinler yalnızca aynı frekansa ayarlanmış oyunculara ulaşır. Acil durum uyarıları için dispatch operatörlerinin ve amirlerin tüm kanallara aynı anda yayın yapabileceği bir öncelikli konuşmacı sistemi uygulayın:
Config.RadioChannels = {
-- Police Department
{ frequency = 1, label = 'PD Main', jobs = {'police'}, type = 'department' },
{ frequency = 2, label = 'PD Patrol', jobs = {'police'}, type = 'department' },
{ frequency = 3, label = 'PD Tactical', jobs = {'police'}, type = 'tactical', maxUsers = 8 },
{ frequency = 4, label = 'PD Detectives', jobs = {'police'}, type = 'tactical', minRank = 3 },
-- EMS / Fire
{ frequency = 10, label = 'EMS Main', jobs = {'ambulance'}, type = 'department' },
{ frequency = 11, label = 'EMS Field', jobs = {'ambulance'}, type = 'department' },
{ frequency = 12, label = 'Fire Main', jobs = {'fire'}, type = 'department' },
-- Inter-department
{ frequency = 20, label = 'Emergency Joint', jobs = {'police','ambulance','fire'}, type = 'inter' },
{ frequency = 21, label = 'Command Channel', jobs = {'police','ambulance','fire'}, type = 'command', minRank = 5 },
-- Civilian (if radio item owned)
{ frequency = 50, label = 'Civilian Band', jobs = {}, type = 'open' },
}
-- Server-side channel state
local radioState = {} -- [frequency] = { players = {}, priority = false }
RegisterNetEvent('radio:server:joinChannel', function(frequency)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local channel = nil
for _, ch in ipairs(Config.RadioChannels) do
if ch.frequency == frequency then channel = ch break end
end
if not channel then return end
-- Check job access
if #channel.jobs > 0 then
local playerJob = Player.PlayerData.job.name
local hasAccess = false
for _, job in ipairs(channel.jobs) do
if job == playerJob then hasAccess = true break end
end
if not hasAccess then
TriggerClientEvent('QBCore:Notify', src, 'No access to this frequency', 'error')
return
end
end
-- Check rank requirement
if channel.minRank and Player.PlayerData.job.grade.level < channel.minRank then
TriggerClientEvent('QBCore:Notify', src, 'Insufficient rank for this channel', 'error')
return
end
-- Check max users for tactical channels
if channel.maxUsers and radioState[frequency] then
if #radioState[frequency].players >= channel.maxUsers then
TriggerClientEvent('QBCore:Notify', src, 'Channel full', 'error')
return
end
end
-- Leave current channel
LeaveCurrentChannel(src)
-- Join new channel
if not radioState[frequency] then
radioState[frequency] = { players = {}, priority = false }
end
table.insert(radioState[frequency].players, src)
TriggerClientEvent('radio:client:joined', src, frequency, channel.label)
TriggerClientEvent('QBCore:Notify', src, 'Tuned to ' .. channel.label .. ' (' .. frequency .. ')', 'success')
end)
10-Kod Sistemi
Ten-code'lar, acil servis iletişimine özgünlük kazandıran ve dispatch sistemi üzerinden hızlı durum güncellemelerine olanak tanıyan standart telsiz kısaltmalarıdır. 10-kodlarını yalnızca basit sohbet makroları olarak uygulamak yerine, memurun dispatch sistemindeki durumunu güncelleyen ve otomatik yanıtları tetikleyen işlevsel komutlar olarak yapılandırın. Bir memur anons geçtiğinde 10-80 (kovalama devam ediyor), sistem dispatch panelindeki durumlarını otomatik olarak güncellemeli, tüm birimler tarafından görülebilen bir kovalama uyarısı oluşturmalı ve memurun aracının GPS takibini başlatmalıdır. Şunu bildirdiklerinde: 10-97 (olay yerine varış), dispatch sistemi onları yanıt verdikleri aktif çağrı için olay yerinde olarak işaretler. Sunucu sahiplerinin kodları departmanlarının tercih ettiği protokole göre özelleştirebilmesi için 10-code sistemini yapılandırılabilir bir tablo olarak oluşturun:
Config.TenCodes = {
['10-4'] = { label = 'Acknowledged', action = nil, status = nil },
['10-6'] = { label = 'Busy', action = nil, status = 'busy' },
['10-7'] = { label = 'Out of Service', action = 'setOffDuty', status = 'off_duty' },
['10-8'] = { label = 'In Service', action = 'setOnDuty', status = 'available' },
['10-11'] = { label = 'Traffic Stop', action = 'createTrafficStop', status = 'traffic_stop' },
['10-15'] = { label = 'Suspect in Custody', action = nil, status = 'transport' },
['10-20'] = { label = 'Location Request', action = 'shareLocation', status = nil },
['10-23'] = { label = 'Arrived at Scene', action = 'markOnScene', status = 'on_scene' },
['10-32'] = { label = 'Person with Weapon', action = 'createAlert', status = 'responding', priority = 'high' },
['10-41'] = { label = 'Beginning Tour of Duty', action = 'clockIn', status = 'available' },
['10-42'] = { label = 'Ending Tour of Duty', action = 'clockOut', status = 'off_duty' },
['10-71'] = { label = 'Shooting', action = 'createAlert', status = 'responding', priority = 'critical' },
['10-78'] = { label = 'Officer Needs Assistance', action = 'panicButton', status = 'emergency', priority = 'critical' },
['10-80'] = { label = 'Pursuit in Progress', action = 'startPursuit', status = 'pursuit', priority = 'high' },
['10-97'] = { label = 'Arriving on Scene', action = 'markOnScene', status = 'on_scene' },
['10-99'] = { label = 'Officer Down', action = 'officerDown', status = 'emergency', priority = 'critical' },
['code4'] = { label = 'No Further Assistance', action = 'clearScene', status = 'available' },
}
RegisterNetEvent('radio:server:tenCode', function(code)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local codeConfig = Config.TenCodes[code]
if not codeConfig then return end
-- Update officer status
if codeConfig.status then
UpdateUnitStatus(src, codeConfig.status)
end
-- Execute associated action
if codeConfig.action then
ExecuteCodeAction(codeConfig.action, src, codeConfig)
end
-- Broadcast to channel
local channel = GetPlayerChannel(src)
if channel then
local name = GetUnitCallsign(src) or Player.PlayerData.charinfo.firstname
BroadcastToChannel(channel, {
type = 'tencode',
code = code,
label = codeConfig.label,
unit = name,
priority = codeConfig.priority,
})
end
end)
Dispatch Çağrı Kuyruğu
Dispatch kuyruğu, gelen tüm acil çağrıları işleyen ve bunları uygun birimlere yönlendiren merkezi merkezdir. Bir sivil 911'i aradığında, bir bankada veya mağazada otomatik bir alarm tetiklendiğinde ya da bir polis memuru destek talep ettiğinde, sistem öncelik düzeyi, konum, açıklama ve gereken hizmet türünü içeren bir dispatch kaydı oluşturur. Kuyruk, dispatch NUI panelinde önceliğe göre sıralanmış tüm aktif çağrıları çağrı zamanı, konum, durum ve hangi birimlerin atandığını gösterecek şekilde listeler. Özel bir dispatch operatörü rolü çağrıları belirli birimlere manuel olarak atayabilir ya da memurlar bir çağrıya tıklayıp kendilerini yanıt veriyor olarak işaretleyerek kendilerini atayabilirler. Yoğun dönemlerde hiçbir acil durumun gözden kaçmamasını sağlamak için, yanıtlanmayan çağrıların yapılandırılabilir bir zaman aşımından sonra önceliğinin arttığı otomatik çağrı yükseltme sistemi uygulayın:
local dispatchQueue = {}
local callIdCounter = 0
function CreateDispatchCall(data)
callIdCounter = callIdCounter + 1
local call = {
id = callIdCounter,
type = data.type or 'general', -- police, ems, fire, general
priority = data.priority or 'standard', -- low, standard, urgent, critical
code = data.code or nil, -- e.g., '10-71', '211' (robbery)
title = data.title,
description = data.description or '',
coords = data.coords,
street = GetStreetName(data.coords),
blip = data.blip or nil,
caller = data.caller or 'Unknown',
callerId = data.callerId or nil,
timestamp = os.time(),
status = 'pending', -- pending, assigned, responding, on_scene, resolved
assignedUnits = {},
escalationTimer = nil,
metadata = data.metadata or {},
}
-- Set escalation timer
if call.priority ~= 'critical' then
call.escalationTimer = os.time() + Config.EscalationTimeout
end
table.insert(dispatchQueue, call)
-- Notify appropriate department
local targetJobs = GetJobsForCallType(call.type)
local players = QBCore.Functions.GetQBPlayers()
for _, player in pairs(players) do
local job = player.PlayerData.job
if job.onduty and TableContains(targetJobs, job.name) then
TriggerClientEvent('dispatch:client:newCall', player.PlayerData.source, call)
end
end
-- Play alert sound based on priority
if call.priority == 'critical' then
PlayDispatchAlert('critical', targetJobs)
end
return call.id
end
-- Automated dispatch triggers
AddEventHandler('banking:robberyStarted', function(bankId, coords)
CreateDispatchCall({
type = 'police',
priority = 'critical',
code = '211',
title = 'Bank Robbery in Progress',
description = 'Silent alarm triggered at ' .. GetBankLabel(bankId),
coords = coords,
blip = { sprite = 500, color = 1, scale = 1.5, flash = true },
})
end)
AddEventHandler('hospital:911call', function(callerId, coords, description, injuries)
CreateDispatchCall({
type = 'ems',
priority = injuries and injuries.maxSeverity >= 80 and 'critical' or 'urgent',
title = 'Medical Emergency',
description = description,
coords = coords,
callerId = callerId,
caller = GetPlayerName(callerId),
blip = { sprite = 153, color = 3, scale = 1.2 },
metadata = { injuries = injuries },
})
end)
GPS Takibi ve Birim Haritası
GPS takibi, tüm aktif birimlerin harita üzerindeki konumlarına gerçek zamanlı görünürlük sağlayarak sevk görevlilerinin ve amirlerin bilinçli görevlendirme kararları almasını sağlar. Görevdeki her memurun konumu, genellikle 5 ila 10 saniyede bir olmak üzere yapılandırılabilir bir aralıkla dispatch sistemine iletilir ve sevk operatörleri ile yetkililerin erişebileceği ortak bir NUI harita panelinde gösterilir. Harita, birim blip'lerini departmana ve duruma göre renk kodlu olarak gösterir; simgeler ise müsait, intikalde, olay yerinde veya takipte olduklarını belirtir. Takip esnasında sevk görevlilerinin hareket yönünü görebilmesi için son birkaç dakikadaki birim hareketlerini izleyen bir iz özelliği ekleyin. Aktif dispatch çağrılarının etrafına önerilen müdahale alanını gösteren bir daire çizen çağrı yarıçapı görselleştirmesini uygulayın. Bir sevk görevlisi yeni bir çağrıya en yakın müsait birimi bulmak istediğinde, sistem tüm uygun birimlere olan mesafeyi hesaplayabilir ve en uygun görevlendirmeyi önerebilir. Yoğun sunucularda ağ trafiğini azaltmak için, yalnızca birim minimum bir mesafe eşiğinden fazla hareket ettiğinde konum güncellemeleri göndererek GPS yayınını verimli tutun.
Uyarı Sistemi ve Panik Butonu
Uyarı sistemi, tüm birimlerin acil müdahalesini gerektiren yüksek öncelikli bildirimleri yönetir. En kritik uyarı, bir memur çağrıda bulunduğunda etkinleştirilen panik butonudur 10-78 veya 10-99, yanıp sönen bir blip ve belirgin bir alarm sesi ile GPS konumlarını görevdeki tüm birimlere gönderir. Panik uyarısı normal dispatch sırası önceliğini geçersiz kılmalı ve onaylanana kadar her memurun ekranında belirgin bir şekilde gösterilmelidir. Kademeli olarak artan yanıtları tetikleyen farklı uyarı seviyeleri uygulayın: standart bir uyarı, bildirim sesiyle bir dispatch çağrısı oluşturur; acil bir uyarı, dispatch panelini yanıp söndürür ve bir uyarı sesi çalar; kritik bir uyarı ise siren sesiyle tam ekran bildirim ve uyarı konumuna otomatik GPS rotası tetikler. Otomatik uyarılar diğer sunucu sistemleriyle entegre olur, böylece silah sesi algılama bölgeleri halka açık alanlarda silah ateşlendiğinde uyarı tetikler, hız kameraları hız sınırını aşan araçları işaretler ve mağaza alarm sistemleri soygunlar başladığında dispatch çağrıları oluşturur. Her uyarı türü, aynı alanda tekrarlanan tetiklemelerden kaynaklanan spam'i önlemek için yapılandırılabilir bekleme sürelerine (cooldown) sahiptir.
Dispatch Operatörü Rolü
Dispatch operatörü, bir iş istasyonunda oturan ve acil durum çağrılarının sahadaki birimlere akışını yöneten özel bir roldür. Basitleştirilmiş bir dispatch bildirimi gören memurların aksine, operatör kuyruk yöneticisi, birim listesi, canlı harita ve iletişim araçlarına sahip eksiksiz bir dispatch konsolu NUI'ına sahiptir. Operatör, çağrıları kuyrukta sürükleyerek önceliklendirebilir, yakınlık ve müsaitlik durumuna göre çağrılara belirli birimleri atayabilir, birimler durumu temiz olarak bildirdiğinde çağrıları çözüldü olarak işaretleyebilir ve departman frekansındaki tüm birimlere anons geçebilir. Dispatch operatörlerinin tam konsola erişmek için belirlenmiş bir dispatch masasında oturmalarını zorunlu kılan bir çalışma istasyonu sistemi uygulayın; bu, sahada bulundukları sırada dispatch sistemini kullanmalarını engeller. Operatör rolü, deneyimli operatörlerin departmanlar arası koordinasyon araçlarına ve acil durum anons yeteneklerine erişim kazandığı kendi meslek rütbesi (job grade) ilerlemesine sahip olmalıdır. Operatörlere performansları hakkında geri bildirim sağlamak ve sunucu yöneticilerine personel alımı kararları için veri sunmak amacıyla ortalama yanıt süresi, vardiya başına yanıtlanan çağrılar ve çağrı çözümleme oranı gibi dispatch performans metriklerini takip edin.
Sesli Telsiz Entegrasyonu
pma-voice veya mumble-voip gibi sesli sohbet çözümleri kullanan sunucularda, gerçekçi radyo iletişimi oluşturmak için telsiz sistemini yakınlık sesiyle entegre edin. Bir oyuncu bir tuşa basılı tutarak telsiz kanalında yayın yaptığında, telsiz iletişimini yüz yüze konuşmadan ayırt etmek için uygulanan bir radyo filtresi efektiyle sesi, fiziksel yakınlığa bakılmaksızın aynı frekanstaki tüm oyuncular tarafından duyulmalıdır. Gerçek telsiz yarı çift yönlü (half-duplex) davranışını simüle ederek başka bir oyuncu konuşmadan önce oyuncuların yayın tuşunu bırakması gereken bir susturma (squelch) sistemi uygulayın. Birisi yayına başladığında ve durdurduğunda sesli geri bildirim sağlamak için tuşa basma ve bırakma anlarına telsiz ses efektleri ekleyin. Ses entegrasyonu, telsiz sisteminde tanımlanan kanal erişim kontrollerine saygı göstermelidir; böylece telsiz NUI'si üzerinden bir frekansa katılmamış oyuncular, mumble kanal kimliğini bir şekilde bilseler bile bu frekansta duyamaz veya yayın yapamaz. Sesli sohbeti olmayan sunucularda, bir frekansta gönderilen mesajların gönderenin çağrı kodu ve kanal tanımlayıcısıyla biçimlendirilmiş bir sohbet formatında göründüğü metin tabanlı bir telsiz sistemine geri dönün.
Entegrasyon ve Genişletilebilirlik
Telsiz ve dispatch sisteminiz, diğer kaynakların dahili uygulamayı anlamalarına gerek kalmadan dispatch çağrıları oluşturmak, uyarılar göndermek ve birim durumunu sorgulamak için kullanabilecekleri temiz bir API sağlamalıdır. Şunun gibi fonksiyonları export edin: CreateDispatchCall, SendAlert, GetAvailableUnits, ve GetUnitStatus böylece sunucudaki herhangi bir kaynak dispatch sistemine entegre olabilir. Bir banka soygunu başladığında, bankacılık kaynağı bir çağrı oluşturmak için dispatch export çağrısı yapar. EMS bir 911 çağrısı aldığında, hastane kaynağı bunu aynı dispatch sistemi üzerinden yönlendirir. Bu merkezi yaklaşım, tüm acil durum iletişiminin tutarlı öncelik yönetimi, birim takibi ve kayıt tutma ile tek bir sistem üzerinden akmasını sağlar. Yönetici incelemesi için dispatch çağrı geçmişini veritabanında saklayın ve her görev dönemi için çağrı hacmini, yanıt sürelerini ve birim etkinliğini özetleyen vardiya raporları oluşturun. Polis vuruldu çağrıları veya banka soygunları gibi kritik uyarıların özel bir yetkili kanalında görünmesi için dispatch sistemini Discord webhooklarına bağlayın: böylece sunucu yöneticileri oyun içinde olmadıklarında bile bilgilendirilir.