Radio z efektami na kanałach
Ten serwer przesyła głos radiowy między graczami na tym samym kanale i nakłada na niego efekt (wokoder 3200, radio analogowe, cyfrowe P25). Mowa zwykła zostaje na pma-voice.
Jak to działa
cs_pdradio + ukryta ramka głosu w NUI, mikrofon, efektaudio 16 kHz
(zgoda na kanał)
- Uprawnienia: gracz nie może wejść na kanał, na który serwer gry mu nie pozwolił. Serwer FiveM prosi serwer webowy o jednorazowy token (ważny 60 s), dopiero z nim gracz łączy się po WebSocket.
- Efekt: liczony w przeglądarce (CEF) każdego odbiorcy, na podstawie jakości sygnału z gry: odległość od wież, strefy martwe, jammery, woda. Słaby sygnał = szum, węższe pasmo, rwanie, brak zasięgu = radio milknie.
- Dwa tryby:
mode = 'web'(to, co opisuje poradnik) albomode = 'pma'(głos radiowy przez pma-voice z efektem GTA, bez serwera webowego).
Test na localhost (bez gry)
Strona działa wyłącznie przez serwer. Nie otwieraj test.html z dysku (file://), bo przeglądarka zablokuje mikrofon i zapytania.
- Zainstaluj Node.js 18 lub nowszyPobierz wersję LTS z nodejs.org. Sprawdź:
node -v
- Uruchom serwerW folderze
webkliknij dwukrotniestart-test.bat. To samo ręcznie:cd web npm install npm start
- Otwórz stronę testowąhttp://localhost:3000/test — wpisz nick i kanał, kliknij „Połącz”, przytrzymaj spację i mów.
- Sprawdź z drugą osobą (albo drugą kartą)Otwórz tę samą stronę w drugiej karcie z tym samym kanałem. Albo w jednej karcie włącz „Słyszę siebie”. Używaj słuchawek.
- Pobaw się efektemZmieniaj preset (Wokoder 3200, Analog, Cyfrowy) i suwak jakości sygnału. Poniżej 45% głos zaczyna się rwać, poniżej 3% radio nie ma zasięgu.
Wdrożenie na VPS (Ubuntu 22.04 / 24.04)
Twój VPS: 49.13.53.85 (Ubuntu). Wystarczy 1 vCPU i 1 GB RAM na kilkadziesiąt osób. Wszystkie polecenia poniżej są już dla tego adresu.
http://49.13.53.85? Menu w grze (NUI) działa na HTTPS, więc przeglądarka w FiveM zablokuje ramkę z http:// oraz mikrofon. Zwykły certyfikat nie wystawia się na sam numer IP, dlatego używamy nazwy 49-13-53-85.sslip.io: to darmowa usługa sslip.io, która sama wskazuje na 49.13.53.85, więc nie musisz kupować domeny. Gdy kiedyś kupisz własną domenę, wystarczy podmienić nazwę w Caddyfile i w config.lua.- Sprawdź nazwę (bez konfiguracji DNS)Nazwa
49-13-53-85.sslip.iood razu wskazuje na49.13.53.85. Sprawdź na swoim komputerze:Powinien wrócić adresnslookup 49-13-53-85.sslip.io
49.13.53.85. - Zaloguj się i zaktualizuj system, włącz zaporęNajpierw połącz się (z komputera): na Twoim komputerze
ssh root@49.13.53.85
na VPSsudo apt update && sudo apt -y upgrade sudo apt -y install ufw curl sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable
Hetzner ma też osobną zaporę w panelu (Cloud Console → Firewalls). Jeśli ją masz, dodaj tam reguły przychodzące TCP 80 i 443 (22 już działa). Bez portu 80 certyfikat się nie wystawi.
Portu 3000 nie otwieraj. Serwer Node nasłuchuje tylko lokalnie, a ruch z internetu przechodzi przez Caddy (HTTPS).
- Zainstaluj Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt -y install nodejs node -v
- Wgraj pliki serweraSkopiuj folder
webz komputera na VPS (z komputera, nie z VPS):na Twoim komputerzescp -r web root@49.13.53.85:/tmp/cs-radio ssh root@49.13.53.85
na VPSsudo mkdir -p /opt/cs-radio sudo cp -r /tmp/cs-radio/. /opt/cs-radio/ cd /opt/cs-radio sudo npm install --omit=dev
- Utwórz plik .env z sekretemSekret to hasło między serwerem FiveM a serwerem webowym. Musi być długi i losowy:
cd /opt/cs-radio SECRET=$(openssl rand -hex 32) echo "$SECRET" # skopiuj, przyda się w sv_config.lua sudo tee .env >/dev/null <<EOF NODE_ENV=production HOST=127.0.0.1 PORT=3000 RADIO_SECRET=$SECRET EOF sudo chmod 600 .env
W trybie
productionserwer odmówi startu, jeśliRADIO_SECRETjest pusty lub równyCHANGE_ME. Endpoint testowy/api/dev-tokenjest wtedy wyłączony. - Uruchom jako usługę (systemd)
/etc/systemd/system/cs-radio.service
[Unit] Description=cs_pdradio web (voice relay) After=network.target [Service] WorkingDirectory=/opt/cs-radio ExecStart=/usr/bin/node server.js Restart=always RestartSec=3 User=www-data Group=www-data NoNewPrivileges=true [Install] WantedBy=multi-user.target
sudo chown -R www-data:www-data /opt/cs-radio sudo systemctl daemon-reload sudo systemctl enable --now cs-radio sudo systemctl status cs-radio --no-pager curl http://127.0.0.1:3000/api/health
- Włącz HTTPS (Caddy, certyfikat automatycznie)
sudo apt -y install debian-keyring debian-archive-keyring apt-transport-https curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update && sudo apt -y install caddy
/etc/caddy/Caddyfile49-13-53-85.sslip.io { encode gzip reverse_proxy 127.0.0.1:3000 }sudo systemctl reload caddy curl https://49-13-53-85.sslip.io/api/health
Caddy sam pobierze certyfikat Let's Encrypt dla
49-13-53-85.sslip.ioi obsłuży WebSocket (wss://). Wolisz nginx? Użyj blokulocation /zproxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";iproxy_pass http://127.0.0.1:3000;.HTTPS jest obowiązkowy. Menu NUI w FiveM działa na HTTPS, więc przeglądarka (CEF) zablokuje ramkę z
http://(mieszana treść) i mikrofon. Wyjątek to wyłącznielocalhost. - Sprawdź z zewnątrzNa swoim komputerze otwórz
https://49-13-53-85.sslip.io/(certyfikat wystawia się przy pierwszym wejściu, daj mu kilkanaście sekund). U góry tej strony status powinien być zielony, ahttps://49-13-53-85.sslip.io/testpozwoli przetestować głos na serwerze produkcyjnym dopiero po zalogowaniu na /live (hasło z instalatora). Bez logowania pokaże komunikat o braku dostępu, to jest zamierzone.
Aktualizacja i logi
sudo journalctl -u cs-radio -f # logi na żywo sudo systemctl restart cs-radio # restart po zmianach # aktualizacja: wgraj nowe pliki do /opt/cs-radio, potem cd /opt/cs-radio && sudo npm install --omit=dev && sudo systemctl restart cs-radio
Podpięcie pod serwer FiveM
- Wgraj zasobySkopiuj do
resources/serwera gry oba foldery zresource/:cs_pdradio(skrypt) ics_pdradio_props(modele: wieża radiowa, anteny, drabinka, przekaźnik komórkowy, serwerownia; 16 MB). Bezcs_pdradio_propswieże nie będą widoczne w świecie, a reszta radia działa normalnie. - Adres dla graczy —
config.luacs_pdradio/config.luaConfig.voice = { mode = 'web', webUrl = 'https://49-13-53-85.sslip.io', -- publiczny adres HTTPS (to już jest ustawione) } - Sekret i adres API —
sv_config.lua(tylko serwer)cs_pdradio/sv_config.luaServerConfig = { -- działa z każdego miejsca (FiveM na VPS lub na innej maszynie): webApi = 'https://49-13-53-85.sslip.io', -- FiveM na TYM SAMYM VPS (49.13.53.85): webApi = 'http://127.0.0.1:3000' secret = 'TU_WKLEJ_SEKRET_Z_ENV', -- identyczny jak RADIO_SECRET }Sekret możesz też ustawić w
server.cfg:set cs_pdradio_secret "..."(ma pierwszeństwo przed plikiem). Pliksv_config.luanigdy nie trafia do graczy. - server.cfg
ensure pma-voice ensure cs_pdradio_props ensure cs_pdradio # administratorzy radia (panel /radioadmin) add_ace group.admin cs_pdradio.admin allow # opcjonalnie: odporność na jammery, uprawnienia do grup kanałów add_ace group.police cs_pdradio.jammerimmune allow
- Uruchom i sprawdźW grze:
/radio, potem/radio power. Na ekranie radia nie powinno być napisu VOICE OFFLINE. W konsoli serwera przy problemie pojawi się komunikat o nieudanymgrant. W panelu/radioadminzakładka Overview pokazuje, czy serwer webowy jest online.
pma-voice. Gdy ktoś nadaje na radiu, osoby obok słyszą go normalnie, jak w prawdziwym życiu.Użycie i panel admina
| Komenda | Opis |
|---|---|
/radio | Pokaż / schowaj radio (animacja z krótkofalówką) |
/radio power | Włącz / wyłącz zasilanie |
/radiovolume 0-100 | Głośność radia |
/radioskin nazwa | Zmiana wyglądu (default, hi-vis, signalpro, voxguard) |
/radiosignal | Aktualna jakość sygnału |
/radiotalk | Włącz / wyłącz animację nadawania |
/radiorepeater | Włącz / wyłącz przekaźnik w pojeździe (jeśli admin go dodał) |
/radiodebug | Stan radia w konsoli F8 (do diagnozy) |
/radioadmin | Panel admina (ACE cs_pdradio.admin) |
PTT (nadawanie) ustawiasz w ustawieniach GTA: Key Bindings → FiveM → Radio: push to talk (domyślnie CapsLock). Kursor nad radiem (żeby klikać przyciski) i inne klawisze ustawiasz tam samo.
Panel admina (/radioadmin)
| Zakładka | Co robisz |
|---|---|
| Overview | Kto jest na radiu i na którym kanale (z możliwością usunięcia), status serwera webowego |
| Repeaters | Pojazdy, które mogą działać jako ruchome wieże (etykieta, zasięg) |
| Towers | Dodajesz wieże w swojej pozycji, ustawiasz zasięg, zdrowie (zniszczone anteny), włączasz i wyłączasz, teleportujesz się do wieży |
| Jammers | Stawiasz jammer w swojej pozycji z wybranym zasięgiem (obiekt w świecie). Radia w zasięgu się rozłączają |
| Dead zones | Strefy osłabiania sygnału (tunele, garaże podziemne) |
| Channels | Grupy i kanały, uprawnienia przez ACE i joby, zapis na żywo |
| Effects | Preset efektu, siła wokodera, szum, sygnały „roger beep”. Zmiana dotyczy wszystkich graczy od razu |
Dane (wieże, jammery, strefy, kanały, efekty) zapisują się w cs_pdradio/data/state.json i wracają po restarcie. Przycisk „Show on map” w stopce panelu rysuje wieże, jammery i strefy na mapie.
Efekty głosu
Efekty odwzorowują to, co naprawdę robi radio, zamiast „losowych” filtrów. Wszystko liczy się w przeglądarce odbiorcy, na podstawie jakości sygnału z gry.
| Preset | Co słychać |
|---|---|
| Analog FM radio | Pasmo 300–3000 Hz, kompresja i miękkie ograniczanie jak w radiu FM, „nosowy” środek pasma. Trzask otwarcia squelcha na początku odbioru, „ogon” szumu po puszczeniu PTT i sygnał końca nadawania (roger beep). |
| Vocoder 3200 | Prawdziwy wokoder LPC (jak kodeki radiowe): głos jest rozkładany na obwiednię widma, wysokość i głośność, a potem syntezowany z buczenia podążającego za wysokością głosu mówcy. Pasmo 300–3200 Hz. Brzmi cyfrowo i metalicznie, ale nadal jak ten człowiek. |
| Digital P25 | Ten sam wokoder, ale łagodniej (część oryginalnego głosu, więcej „oddechu”), pasmo 300–3400 Hz. |
| Clean | Prawie bez efektu, tylko szerszy filtr. |
Zasięg: jak psuje się sygnał
| Analog FM | Cyfra / wokoder | |
|---|---|---|
| Dobry sygnał | Cisza w tle, czysty głos (FM „wycisza” szum) | Czysty głos kodeka |
| Średni | Pojawia się szum eteru, głos lekko „faluje” (zanikanie wielodrogowe) | Pierwsze błędy ramek: pojedyncze „bulgotanie” |
| Słaby | Silny szum, głębokie falowanie, trzaski „popcorn” przy granicy zasięgu, krótkie wypadnięcia | Powtarzane i wyciszane ramki, głos się „kruszy”, bez szumu |
| Brak (< 3%) | Radio nie nadaje i nie odbiera (NO SIGNAL) | |
Wieże, przekaźniki i animacje
| Co | Jak działa |
|---|---|
| Fizyczne wieże | Wieże z panelu (Towers) pojawiają się w świecie jako model z antenami i drabinką. Liczba anten zależy od „Health”: 100% = 4, 75% = 3, 50% = 2, 25% = 1, 0% = brak. Włączasz to w Config.towers.props. |
| Niszczenie i naprawa | Z Config.towers.destruction = true zestrzelona antena obniża zdrowie wieży o 25% dla wszystkich. Naprawa: stań przy wieży i naciśnij G (spawanie ok. 6 s). Uprawnienie do napraw ustawisz w Config.towers.repairAce. |
| Przekaźniki w pojazdach | W panelu Repeaters dodajesz pojazd (usiądź w nim lub stań obok, np. przyczepa mobilecell). Gracz w takim pojeździe włącza go komendą /radiorepeater, a pojazd działa jak ruchoma wieża z zadanym zasięgiem. Wyłącza się sam po wyjściu z pojazdu. |
| Animacje | Otwarcie radia pieszo: krótkofalówka w dłoni i animacja telefonu (jak w oryginale; w pojeździe bez animacji). Nadawanie: z otwartym radiem pozycja „rozmowa”, z zamkniętym animacja z ramieniem przy ustach. Gracz wyłącza ją komendą /radiotalk. |
| HUD | Gdy radio jest włączone, a duże okno schowane, w rogu ekranu widać mini radio z grafiką skina: kanał, status (kto nadaje) i zasięg. Wyłączysz w Config.hud. |
Modele pochodzą z oryginalnego radia (folder cs_pdradio_props). Nie uruchamiaj ich razem ze starym zasobem z tymi samymi modelami, bo nazwy pojazdów (sonoran, mobilecell, serverrack) by się dublowały.
Dźwięki tła w mikrofonie (syrena, helikopter, strzały)
Gdy trzymasz PTT, do nadawanego głosu dochodzi to, co dzieje się wokół Ciebie. Słyszą to wszyscy na kanale, a radio spłaszcza i przesterowuje to tak jak prawdziwe.
| Sytuacja | Co słychać pod głosem |
|---|---|
| Radiowóz z włączoną syreną | Syrena (przemiatanie 0,7–1,5 kHz). Cichsza, gdy stoisz obok pojazdu pieszo, bardzo cicha w helikopterze |
| Samochód / motor / łódź | Silnik i wiatr, głośniejsze wraz z prędkością |
| Helikopter | Uderzenia wirnika (ok. 21 Hz), wycie turbiny, wiatr. Samolot: ciągły szum silnika |
| Strzały podczas nadawania | Trzask i huk wchodzą do mikrofonu, kompresor radia na moment przycisza głos |
Skrypt Lua sam wykrywa typ pojazdu, syrenę (tylko z włączonym dźwiękiem), prędkość i strzały i wysyła to do radia. Efekt sprawdzisz na stronie /test (wybór pojazdu, syrena, prędkość, przyciski „Strzał” i „Seria”, a „Słyszę siebie” pokaże to, co słyszą inni).
Ekran radia: styl Windows 98
Ekran radia wygląda jak okno Windows 98 i ma osobny układ zależnie od tego, gdzie jesteś:
| Gdzie | Układ |
|---|---|
| Pieszo (ręczna krótkofalówka) | Wąskie okno: grupa, lista kanałów, głośność, zasięg, przyciski Prev / Next / Panic |
| W pojeździe (radio mobilne) | Szersze okno: lista kanałów po lewej, panel statusu po prawej (grupa, kanał, zasięg, głośność) |
| Helikopter / samolot | Największe okno: kanały, radio oraz panel nawigacji (kurs, wysokość, prędkość) |
Grafiki obudów (krótkofalówka, radio mobilne, panel lotniczy) pochodzą z oryginalnych skinów w skins/. Przyciski na ekranie klikasz po włączeniu kursora (klawisz „Radio: mouse cursor” albo przycisk □ w oknie). Komenda /radiodebug wypisuje stan radia w konsoli F8.
Zagłuszanie i trzaski
| Źródło | Skąd się bierze (Lua) | Jak brzmi |
|---|---|---|
| Wieże, strefy martwe, woda | Skrypt liczy jakość sygnału z odległości od wież (panel → Towers), stref i pływania | Szum, falowanie i trzaski (analog) albo błędy ramek (cyfra), aż do braku zasięgu |
| Jammer | Panel → Jammers. Poziom zagłuszania rośnie od ok. 1,8× zasięgu do środka | Szum na kanale, gwizd z przemiataniem i buczenie 100 Hz, głos coraz bardziej zagłuszony. W zasięgu jammera radio jest martwe, ale dalej słychać zakłócenia |
| Pogoda | Burza i deszcz w grze (Config.interference.weather) | Szum tła i losowe trzaski podczas odbioru |
| Inne skrypty | exports['cs_pdradio']:setExtraInterference('emp', 0.8) (0 usuwa) | Trzaski na wszystkich radiach (EMP, rozbłysk słoneczny) |
Odpowiedź na pytanie „czy to zależy od wież?”: tak, jakość sygnału (zasięg) liczy skrypt Lua po stronie gracza z danych o wieżach, strefach i wodzie, a serwer webowy tylko ją odtwarza jako dźwięk. Zagłuszanie jammerem i trzaski pogodowe to osobne źródła zakłóceń, niezależne od wież. Poziomy sprawdzisz w grze komendą /radiosignal.
Przytłumienie (muffling)
Każdy preset ma parametr muffle (0–100%), który ścina wysokie tony i zmiękcza środek pasma. Vocoder 3200 ma go domyślnie ustawionego mocniej (ciemniejsze, „przytłumione” brzmienie). Zmienisz go w /radioadmin → Effects (suwak „Muffling”) lub na stronie /test.
Liczy się gorszy sygnał z dwóch: nadawcy i odbiorcy. Szum eteru działa tylko wtedy, gdy ktoś nadaje (jak przy zamkniętym squelchu).
Strojenie: w panelu /radioadmin → Effects ustawiasz preset, siłę wokodera i szum. Własne wartości (pasmo, zniekształcenie, squelch itd.) są w public/radio-client.js w obiekcie PRESETS, a sam kodek w public/vocoder.js. Zmiany w tych plikach sprawdzisz od razu na stronie /test.
Rozwiązywanie problemów
| Objaw | Przyczyna i rozwiązanie |
|---|---|
| Na ekranie VOICE OFFLINE, w konsoli „web server grant failed (HTTP 401)” | Sekret w sv_config.lua (lub convar) różni się od RADIO_SECRET w .env. Ustaw identyczne i zrestartuj cs_pdradio oraz cs-radio. |
HTTP 0 / timeout przy grant | Zły webApi albo serwer webowy nie działa. Z maszyny FiveM wykonaj curl <webApi>/api/health. |
| Radio działa, ale nic nie słychać | W konsoli F8 / DevTools sprawdź, czy ramka głosu się ładuje. Częsta przyczyna: webUrl to http:// zamiast https:// (mieszana treść). |
| „Radio: microphone access denied” | Ramka musi być z HTTPS (lub localhost). Sprawdź też, czy w systemie jest ustawiony domyślny mikrofon. |
| Caddy: „too many certificates” / limit Let's Encrypt dla sslip.io | Usługa sslip.io jest współdzielona i czasem trafia na limity wystawiania certyfikatów. Rozwiązanie: darmowa nazwa z duckdns.org (np. twojradio.duckdns.org → 49.13.53.85) albo własna domena. Podmień nazwę w /etc/caddy/Caddyfile i w config.lua (webUrl). |
| Certyfikat się nie wystawia (Caddy) | Rekord DNS nie wskazuje jeszcze na VPS albo porty 80/443 są zablokowane (zapora lub panel dostawcy VPS). |
| Rozłącza co kilka sekund | Proxy ucina WebSocket. W nginx dodaj nagłówki Upgrade i Connection oraz proxy_read_timeout 3600s;. Caddy robi to sam. |
| Słyszę siebie z opóźnieniem | To loopback tylko na stronie testowej. W grze nadawca siebie nie słyszy. |
| Nie mogę wejść na kanał | Brak uprawnień do grupy (job / ACE) albo kanał nie istnieje. Sprawdź zakładkę Channels. Admin ma dostęp do wszystkich. |
npm install nie działa | Za stary Node (potrzebny 18+). Sprawdź node -v. |
Bezpieczeństwo
- Nigdy nie wpisuj sekretu do
config.lua(ten plik trafia do graczy). Tylkosv_config.luaalbo convar. - Zostaw
HOST=127.0.0.1i wystawiaj serwer wyłącznie przez Caddy / nginx z HTTPS. - Tokeny wejścia są jednorazowe i wygasają po 60 s; jedno połączenie na gracza; limit przepustowości i rozmiaru ramki na klienta.
- Jakość sygnału przesyła klient. Złośliwy gracz może więc zgłosić lepszy sygnał dla własnego głosu. To tylko kosmetyka efektu, nie omija uprawnień do kanałów.
- Strona testowa i
/api/dev-tokendziałają tylko pozaNODE_ENV=production.