From 0b1c0b2e8c51477539fa79eed6ae5e3abbb31f72 Mon Sep 17 00:00:00 2001 From: srslyyyy <51768772+srslyyyy@users.noreply.github.com> Date: Mon, 15 Jul 2024 04:23:03 +0200 Subject: [PATCH 1/2] internetradio: refactor (#507) --- [gameplay]/internetradio/client.lua | 353 ------------------ .../internetradio/config/CRadioConfig.lua | 24 ++ .../internetradio/config/SRadioConfig.lua | 7 + .../internetradio/config/ShRadioConfig.lua | 98 +++++ [gameplay]/internetradio/gui/CRadioGUI.lua | 80 ++++ .../handle_radio/CHandleRadio.lua | 331 ++++++++++++++++ .../handle_radio/SHandleRadio.lua | 170 +++++++++ .../handle_radio/ShHandleRadio.lua | 95 +++++ [gameplay]/internetradio/meta.xml | 10 +- [gameplay]/internetradio/server.lua | 145 ------- .../internetradio/settings/CRadioSettings.lua | 117 ++++++ .../track_name/CRadioTrackName.lua | 114 ++++++ 12 files changed, 1042 insertions(+), 502 deletions(-) delete mode 100644 [gameplay]/internetradio/client.lua create mode 100644 [gameplay]/internetradio/config/CRadioConfig.lua create mode 100644 [gameplay]/internetradio/config/SRadioConfig.lua create mode 100644 [gameplay]/internetradio/config/ShRadioConfig.lua create mode 100644 [gameplay]/internetradio/gui/CRadioGUI.lua create mode 100644 [gameplay]/internetradio/handle_radio/CHandleRadio.lua create mode 100644 [gameplay]/internetradio/handle_radio/SHandleRadio.lua create mode 100644 [gameplay]/internetradio/handle_radio/ShHandleRadio.lua delete mode 100644 [gameplay]/internetradio/server.lua create mode 100644 [gameplay]/internetradio/settings/CRadioSettings.lua create mode 100644 [gameplay]/internetradio/track_name/CRadioTrackName.lua diff --git a/[gameplay]/internetradio/client.lua b/[gameplay]/internetradio/client.lua deleted file mode 100644 index 7ee0c8dd5..000000000 --- a/[gameplay]/internetradio/client.lua +++ /dev/null @@ -1,353 +0,0 @@ -local radioSpeakers = {} -local blackColor = tocolor(0, 0, 0, 255) -local textColor = tocolor(150, 50, 150, 255) -local radioTable = { - {"[Top 40 Mix] BAYERN Radio - Top40", "http://stream.antenne.de:80/top-40"}, - {"[Top 40 Mix] 1.FM - Absolute Top 40", "http://185.33.21.111:80/top40_32a"}, - {"[Top 40 Pop] Power 181", "http://listen.181fm.com/181-power_128k.mp3"}, - {"[80s] BAYERN Radio - 80ers", "http://stream.antenne.de:80/80er-kulthits"}, - {"[90s] BAYERN Radio - 90ers", "http://stream.antenne.de:80/90er-hits"}, - {"[RAP] RadioRecord RapClassics", "https://radiorecord.hostingradio.ru/rapclassics96.aacp"}, - {"[RAP] 1001 The Heat", "http://149.56.157.81:8569/listen.pls"}, - {"[HipHop] HOT 108 JAMZ", "https://live.powerhitz.com/hot108"}, - {"[Mix] Antena1 94.7", "http://51.254.29.40:80/stream2"}, - {"[Mix] Mix96", "https://radiorecord.hostingradio.ru/mix96.aacp"}, - {"[Mix] Radio SA", "https://y0b.net/radiosa.m3u"}, - {"[Mix] 181 The Mix", "http://listen.livestreamingservice.com/181-themix_64k.aac"}, - {"[DANCE] Dance One", "http://185.33.21.112:80/dance_128"}, - {"[DANCE] Dance Wave", "https://dancewave.online/dance.aac"}, - {"[DANCE] Dance Wave Retro", "https://retro.dancewave.online/retrodance.aac"}, - {"[DANCE] 1.FM - Deep House", "http://185.33.21.111:80/deephouse_64a"}, - {"[POP] Antenne Bayern", "http://stream.antenne.de:80/antenne"}, - {"[Roadman] UK DRILL RAP", "https://stream.zeno.fm/bmqy8pp9am8uv"}, - {"[Roadman] UK DRILL FM", "https://stream.zeno.fm/zbhn3mx74bhvv"}, - {"[Ghetto] Alabama's Finest", "http://66.85.47.227:8000/stream"}, - {"[HardBass] Russian HardBASS", "https://radiorecord.hostingradio.ru/hbass96.aacp"}, - {"[Trap] RadioRecord Trap96", "https://radiorecord.hostingradio.ru/trap96.aacp"}, - {"[Drift] Phonk Radio", "https://s2.radio.co/s2b2b68744/listen"}, - {"[Drift] Phonk Radio #2", "https://radiorecord.hostingradio.ru/phonk96.aacp"}, - {"[Drift] Phonk Radio #3", "https://stream.zeno.fm/lfrqotftczpuv"}, - {"[Drift] Phonk Radio #4", "https://stream.zeno.fm/ym4ywb2ezs8uv"}, - {"[Drift] Phonk Radio #5", "https://stream.zeno.fm/aeoju66zrnfuv"}, - {"[Drift] Phonk Radio #6", "https://stream.zeno.fm/71ntub27u18uv"}, - {"[Dubstep] Dub96", "https://radiorecord.hostingradio.ru/dub96.aacp"}, - {"[Random] 105.3 Easy POP and 90s", "http://157.90.133.87:8076/stream"}, - {"[House] True House Chill", "http://stream.truehouse.net:8000/chill"}, - {"[RnB Hits] PowerHitz", "http://66.85.88.174/realrnb"}, - {"[ROCK] BAYERN Radio - 90er Rock", "http://stream.antenne.de:80/90er-rock"}, - {"[ROCK] BAYERN Radio - Classic Rock", "http://stream.antenne.de:80/classic-rock-live"}, - {"[ROCK] 1.FM Classic Rock", "http://185.33.21.111:80/crock_64a"}, - {"[PT Radio & International] Antena1", "https://www.antena1.com.br/stream/"}, - {"[PT Radio] AlienWare", "https://sonic.paulatina.co:10995"}, -- http://elhuecodelasalsa.com/ - {"[Random] 100 Hit Radio", "https://radio1.streamserver.link/radio/8010/100hit-aac"}, - {"[Office] Office Hitz", "http://66.85.88.174/officemix"}, - {"[Pop] Antena1 - 94.7fm", "http://5.135.83.159:80/stream2"}, - {"[Blues] XRDS Blues", "http://198.58.106.133:8321/stream"}, - {"[RU] RussianMix", "https://radiorecord.hostingradio.ru/rus96.aacp"}, - {"[Jazz] Bay Smooth Jazz", "http://185.33.21.111:80/smoothjazz_64a"}, - {"[Trance] Amsterdam Trance", "http://185.33.21.111:80/atr_128"}, - {"[Trance] Absolute Trance Euro", "http://185.33.21.112:80/trance_128"}, - {"[Lovesongs] 100Hits Love", "https://194.97.151.139/lovesongs"}, - {"[ARAB] Arab HitsRadio", "https://icecast.omroep.nl/funx-arab-bb-mp3"}, - {"[ARAB] Arabesk FM", "https://yayin.arabeskfm.biz:8042/"}, - {"[90's] Star 90's", "http://listen.181fm.com/181-star90s_128k.mp3"}, - {"[Random] Hitradio OE3", "https://orf-live.ors-shoutcast.at/oe3-q1a.m3u"}, - {"[Jewish Radio] Jewish Music Stream", "https://stream.jewishmusicstream.com:8000/stream"}, - {"[Thailand Radio] COOLFahrenheit93", "http://103.253.132.7:5004"}, - {"[India] Bollywood Hits Radio", "https://stream.zeno.fm/rqqps6cbe3quv"}, - {"[Russia Radio 1] TatarRadiosi", "https://tatarradio.hostingradio.ru/tatarradio320.mp3"}, - {"[Russia Radio 2] Shanson", "https://chanson.hostingradio.ru:8041/chanson256.mp3"}, - {"[Russia Radio 3] EuropaRussia", "https://europarussia.ru:8006/live"}, - {"[Russia Radio 4] Blatnyachok", "http://89.188.115.214:8000"}, - {"[80s] Russian & International 80s", "https://radiorecord.hostingradio.ru/198096.aacp"}, - {"[90s] Russian & International 90s", "https://radiorecord.hostingradio.ru/sd9096.aacp"}, - {"[Top] 1.FM Hits 2000-s", "http://185.33.21.111:80/hits2000_128"}, - {"[2000s] BAYERN Radio - 2000-s", "http://stream.antenne.de:80/2000er-hits"}, - {"[2010s] BAYERN Radio - 2010-s", "http://stream.antenne.de:80/2010er-hits"}, - {"[70s] SomaFM Seventies", "https://ice1.somafm.com/seventies-128-aac"}, - {"[80s] BAYERN Radio - 80ers", "http://stream.antenne.de:80/80er-kulthits"}, - {"[90s] BAYERN Radio - 90ers", "http://stream.antenne.de:80/90er-hits"}, -- Alternative: http://s1-webradio.antenne.de/90er-hits - {"[Top] BAYERN Radio - Top 1000", "http://stream.antenne.de:80/top-1000"}, - {"[Random] BAYERN Radio - Chillout", "http://stream.antenne.de:80/chillout"}, - {"[Random] BAYERN Radio - Relax", "http://stream.antenne.de:80/relax"}, - {"[Random] BAYERN Radio - Lounge", "http://stream.antenne.de:80/lounge"}, - {"[Random] BAYERN Radio - In The Mix", "http://stream.antenne.de:80/in-the-mix"}, - {"[Random] BAYERN Radio - Spring Hits", "http://stream.antenne.de:80/fruehlings-hits"}, - {"[Random] BAYERN Radio - Happy Hits", "http://stream.antenne.de:80/happy-hits"}, - {"[Random] BAYERN Radio - Greatest Hits", "http://stream.antenne.de:80/greatest-hits"}, - {"[Random] BAYERN Radio - Party Hits", "http://stream.antenne.de:80/party-hits"}, - {"[Random] BAYERN Radio - Pop XXL", "http://stream.antenne.de:80/pop-xxl"}, - {"[Dance / RU] RadioRecord", "https://radiorecord.hostingradio.ru/rr_main96.aacp"}, - {"[Deep96] RadioRecord", "https://radiorecord.hostingradio.ru/deep96.aacp"}, - {"[Mix96] RadioRecord", "https://radiorecord.hostingradio.ru/mix96.aacp"}, - {"[Uplift96] RadioRecord", "https://radiorecord.hostingradio.ru/uplift96.aacp"}, - {"[Ambient96] RadioRecord", "https://radiorecord.hostingradio.ru/ambient96.aacp"}, - {"[Darkside96] RadioRecord", "https://radiorecord.hostingradio.ru/darkside96.aacp"}, - {"[Summer96] RadioRecord", "https://radiorecord.hostingradio.ru/summerparty96.aacp"}, - {"[Dance] BAYERN Radio - Dance XXL", "http://stream.antenne.de:80/dance-xxl"}, - {"[Dance] FunX Dance", "https://icecast.omroep.nl/funx-dance-bb-mp3"}, -} - -function speakersysGUI() - if (speakersys) then - return true - end - - local screenX, screenY = guiGetScreenSize() - - speakersys = guiCreateWindow((screenX - 325) / 1.1, (screenY - 380) / 1.4, 325, 380, "SPEAKER MUSIC (RADIO/MP3)", false) - guiSetAlpha(speakersys, 1) - guiWindowSetSizable(speakersys, false) - urlGrid = guiCreateGridList(10, 54, 304, 139, false, speakersys) - urlEdit = guiCreateEdit(10, 25, 304, 26, "http://stream.antenne.de:80/80er-kulthits", false, speakersys) - setBtn = guiCreateButton(10, 200, 150, 30, "CREATE SPEAKER", false, speakersys) - destroyBox = guiCreateButton(162, 200, 150, 30, "DESTROY SPEAKER", false, speakersys) - pauseBtn = guiCreateButton(10, 235, 150, 30, "Play - Pause", false, speakersys) - close = guiCreateButton(162, 235, 150, 30, "Close", false, speakersys) - - xmlSetting = guiCreateCheckBox(15, 345, 160, 17, "Toggle other boxes", false, false, speakersys) - enableLabel = guiCreateLabel(167, 345, 150, 17, "", false, speakersys) - urlColumn = guiGridListAddColumn(urlGrid, "Internet Radio Station", 0.8) - guiSetVisible(speakersys, false) - - if (not getSetting("box") or getSetting("box") == "ENABLED") then - enable = true - guiCheckBoxSetSelected(xmlSetting, true) - setSetting("box", "ENABLED") - guiSetText(enableLabel, "You hear all speakers") - guiLabelSetColor(enableLabel, 0, 255, 0) - elseif getSetting("box") == "DISABLED" then - enable = nil - guiCheckBoxSetSelected(xmlSetting, false) - guiSetText(enableLabel, "You only hear your speaker") - guiLabelSetColor(enableLabel, 255, 0, 0) - end - - addEventHandler("onClientGUIClick", urlGrid, updateUrlEdit, false) - - for key, ent in pairs(radioTable) do - local row = guiGridListAddRow(urlGrid) - guiGridListSetItemText(urlGrid, row, urlColumn, ent[1], false, false) - end - setTimer(getRadioInfo, 10000, 0) -end -addEventHandler("onClientResourceStart", resourceRoot, speakersysGUI) - -function updateUrlEdit() - if (guiGridListGetItemText(urlGrid, guiGridListGetSelectedItem(urlGrid), 1) ~= "") then - for key, ent in pairs(radioTable) do - if (guiGridListGetItemText(urlGrid, guiGridListGetSelectedItem(urlGrid), 1) == ent[1]) then - guiSetText(urlEdit, ent[2]) - end - end - end -end - -function openGUI() - if (guiGetVisible(speakersys)) then - guiSetVisible(speakersys, false) - showCursor(false) - else - guiSetVisible(speakersys, true) - showCursor(true) - end -end -addEvent("Speaker.openInterface", true) -addEventHandler("Speaker.openInterface", root, openGUI) -bindKey("F3", "down", openGUI) - -function clickEvent() - if (source == setBtn) then - local vol = 1.0 - local url = guiGetText(urlEdit) - - if not (url) or (url == "") or (string.len(url) > 550) or not string.find(url, "http") then - return outputChatBox("SPEAKER: Invalid URL, please check your input!", 255, 0, 0) - end - - triggerServerEvent("speaker.startStream", localPlayer, url, vol) - - elseif (source == close) then - openGUI() - elseif (source == pauseBtn) then - triggerServerEvent("speaker.pause", localPlayer) - elseif (source == destroyBox) then - triggerServerEvent("speaker.destroy", localPlayer) - elseif (source == xmlSetting) then - enable = not enable - - if (enable) then - outputChatBox("NOTE: Reconnect to listen to deactivated songs again", 0, 255, 0) - setSetting("box", "ENABLED") - guiCheckBoxSetSelected(xmlSetting, true) - guiSetText(enableLabel, "You hear all speakers") - guiLabelSetColor(enableLabel, 0, 255, 0) - else - setSetting("box", "DISABLED") - guiCheckBoxSetSelected(xmlSetting, false) - guiSetText(enableLabel, "You only hear your music") - guiLabelSetColor(enableLabel, 255, 0, 0) - end - end -end -addEventHandler("onClientGUIClick", resourceRoot, clickEvent) - -function setData(value, theType, serial) - if (not radioSpeakers[serial]) then - return - end - - if (not isElement(radioSpeakers[serial][1])) then - return - end - - if (theType == "vol") then - setSoundVolume(radioSpeakers[serial][1], value) - elseif (theType == "dist") then - setSoundMaxDistance(radioSpeakers[serial][1], 65) - elseif (theType == "destroy") then - stopSound(radioSpeakers[serial][1]) - destroyElement(radioSpeakers[serial][2]) - radioSpeakers[serial] = nil - end -end -addEvent("speaker.setData", true) -addEventHandler("speaker.setData", root, setData) - -function setPaused(serial) - if (not radioSpeakers[serial]) then - return - end - - if (not isElement(radioSpeakers[serial][1])) then - return - end - setSoundPaused(radioSpeakers[serial][1], not isSoundPaused(radioSpeakers[serial][1])) -end -addEvent("speaker.ps", true) -addEventHandler("speaker.ps", root, setPaused) - -function setBox(str) - if (not str) then - return - end - - for serial, ent in pairs(str) do - - if (radioSpeakers[serial] and isElement(radioSpeakers[serial][1])) then - destroyElement(radioSpeakers[serial][1]) - destroyElement(radioSpeakers[serial][2]) - end - - if (isElement(ent[1])) then - if (ent[11] == getPlayerName(localPlayer) or enable) then - local radio = playSound3D(ent[2], ent[3], ent[4], ent[5], true, false) - setElementData(radio, "ob", ent[11]) - local dumm = createObject(1337, ent[3], ent[4], ent[5]) - setElementAlpha(dumm, 0) - setElementCollisionsEnabled(dumm, false) - attachElements(dumm, ent[1], -0.32, -0.22, 0.8) - radioSpeakers[serial] = {radio, dumm} - - if (ent[6] and isElement(ent[6])) then - attachElements(radio, ent[6]) - end - - setElementInterior(radio, ent[7]) - setElementDimension(radio, ent[8]) - setSoundVolume(radio, ent[9]) - setSoundMaxDistance(radio, ent[10]) - setInteriorSoundsEnabled(false) - end - end - end -end -addEvent("speaker.setBox", true) -addEventHandler("speaker.setBox", root, setBox) - -function getRadioInfo() - for key, radio in pairs(radioSpeakers) do - if (radio[1] and isElement(radio[1])) then - radioSpeakers[key][3] = nil - table.insert(radioSpeakers[key], getSoundMetaTags(radio[1]).stream_title or getSoundMetaTags(radio[1]).title) - end - end -end - -function drawData() - for key, radio in pairs(radioSpeakers) do - if (radio[2] and isElement(radio[2] and radio[1])) then - local eX, eY, eZ = getElementPosition(radio[2]) - eZ = (eZ + 1) - local sx, sy = getScreenFromWorldPosition(eX, eY, eZ) - local cameraX, cameraY, cameraZ = getCameraMatrix() - - if (sx and sy) then - if (not enable and getElementData(radio[1], "ob") ~= getPlayerName(localPlayer)) then - stopSound(radio[1]) - destroyElement(radio[2]) - radioSpeakers[key] = nil - return false - end - - local distance = getDistanceBetweenPoints3D(cameraX, cameraY, cameraZ, eX, eY, eZ) - - if (distance <= 65 and isLineOfSightClear(cameraX, cameraY, cameraZ, eX, eY, eZ, true, false, false, false, false, false) and radio[3]) then - local height = dxGetFontHeight(1, "default-bold") - local owner = getElementData(radio[1], "ob") - text = radio[3] - - if toggle then - text = "Owner: " .. owner .. " - " .. text .. "" - end - - local width = dxGetTextWidth(text, 1, "default-bold") - - dxDrawRectangle(sx - width / 2 - 5, sy, width + 8, height, blackColor, false) - dxDrawText(text, sx - width / 2, sy, sx - width / 2, sy, textColor, 1, "default-bold") - end - end - end - end -end -addEventHandler("onClientRender", root, drawData) - -function toggleCursor(key, state) - toggle = not toggle -end -bindKey("lalt", "both", toggleCursor) - -settingsXMLFile = nil - -function onStartup() - triggerServerEvent("getSpeakers", localPlayer) - - settingsXMLFile = xmlLoadFile("settings.xml") - if not settingsXMLFile then - settingsXMLFile = xmlCreateFile("settings.xml", "settings") - end -end -addEventHandler("onClientResourceStart", resourceRoot, onStartup) - -onStartup() - -function setSetting(setting, value) - if value then - value = tostring(value) - xmlNodeSetAttribute(settingsXMLFile, setting, value) - end - xmlSaveFile(settingsXMLFile) -end - -function getSetting(setting) - if setting then - local val = xmlNodeGetAttribute(settingsXMLFile, setting) - if val then - return val - else - return false - end - else - return false - end -end diff --git a/[gameplay]/internetradio/config/CRadioConfig.lua b/[gameplay]/internetradio/config/CRadioConfig.lua new file mode 100644 index 000000000..074933b58 --- /dev/null +++ b/[gameplay]/internetradio/config/CRadioConfig.lua @@ -0,0 +1,24 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +RADIO_TRACK_SCALE = 1 +RADIO_TRACK_FONT = "default-bold" +RADIO_TRACK_COLOR = tocolor(150, 50, 150, 255) +RADIO_TRACK_BACKGROUND_COLOR = tocolor(0, 0, 0, 255) + +RADIO_TOGGLE_KEY = "F3" +RADIO_SHOW_ON_START = true +RADIO_COMMANDS = {"sound", "music", "musica", "song", "radio", "speaker"} +RADIO_SETTINGS_PATH = "settings.json" + +RADIO_SETTINGS_TEMPLATE = { + ["allowRemoteSpeakers"] = { + defaultsTo = true, + dataType = { + ["boolean"] = true, + }, + }, +} \ No newline at end of file diff --git a/[gameplay]/internetradio/config/SRadioConfig.lua b/[gameplay]/internetradio/config/SRadioConfig.lua new file mode 100644 index 000000000..4bfae1e9c --- /dev/null +++ b/[gameplay]/internetradio/config/SRadioConfig.lua @@ -0,0 +1,7 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +RADIO_BOX_MODEL = 2229 \ No newline at end of file diff --git a/[gameplay]/internetradio/config/ShRadioConfig.lua b/[gameplay]/internetradio/config/ShRadioConfig.lua new file mode 100644 index 000000000..9034d70ad --- /dev/null +++ b/[gameplay]/internetradio/config/ShRadioConfig.lua @@ -0,0 +1,98 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +RADIO_CREATE_SPEAKER_DELAY = 3000 +RADIO_TOGGLE_SPEAKER_DELAY = 1500 +RADIO_DESTROY_SPEAKER_DELAY = 1500 +RADIO_ALLOW_CUSTOM_URLS = false +RADIO_STREAM_URL_MAX_LENGTH = 512 +RADIO_MAX_SOUND_DISTANCE = 65 +RADIO_STATIONS = { + {"[Top 40 Mix] BAYERN Radio - Top40", "http://stream.antenne.de:80/top-40"}, + {"[Top 40 Mix] 1.FM - Absolute Top 40", "http://185.33.21.111:80/top40_32a"}, + {"[Top 40 Pop] Power 181", "http://listen.181fm.com/181-power_128k.mp3"}, + {"[80s] BAYERN Radio - 80ers", "http://stream.antenne.de:80/80er-kulthits"}, + {"[90s] BAYERN Radio - 90ers", "http://stream.antenne.de:80/90er-hits"}, + {"[RAP] RadioRecord RapClassics", "https://radiorecord.hostingradio.ru/rapclassics96.aacp"}, + {"[RAP] 1001 The Heat", "http://149.56.157.81:8569/listen.pls"}, + {"[HipHop] HOT 108 JAMZ", "https://live.powerhitz.com/hot108"}, + {"[Mix] Antena1 94.7", "http://51.254.29.40:80/stream2"}, + {"[Mix] Mix96", "https://radiorecord.hostingradio.ru/mix96.aacp"}, + {"[Mix] Radio SA", "https://y0b.net/radiosa.m3u"}, + {"[Mix] 181 The Mix", "http://listen.livestreamingservice.com/181-themix_64k.aac"}, + {"[DANCE] Dance One", "http://185.33.21.112:80/dance_128"}, + {"[DANCE] Dance Wave", "https://dancewave.online/dance.aac"}, + {"[DANCE] Dance Wave Retro", "https://retro.dancewave.online/retrodance.aac"}, + {"[DANCE] 1.FM - Deep House", "http://185.33.21.111:80/deephouse_64a"}, + {"[POP] Antenne Bayern", "http://stream.antenne.de:80/antenne"}, + {"[Roadman] UK DRILL RAP", "https://stream.zeno.fm/bmqy8pp9am8uv"}, + {"[Roadman] UK DRILL FM", "https://stream.zeno.fm/zbhn3mx74bhvv"}, + {"[Ghetto] Alabama's Finest", "http://66.85.47.227:8000/stream"}, + {"[HardBass] Russian HardBASS", "https://radiorecord.hostingradio.ru/hbass96.aacp"}, + {"[Trap] RadioRecord Trap96", "https://radiorecord.hostingradio.ru/trap96.aacp"}, + {"[Drift] Phonk Radio", "https://s2.radio.co/s2b2b68744/listen"}, + {"[Drift] Phonk Radio #2", "https://radiorecord.hostingradio.ru/phonk96.aacp"}, + {"[Drift] Phonk Radio #3", "https://stream.zeno.fm/lfrqotftczpuv"}, + {"[Drift] Phonk Radio #4", "https://stream.zeno.fm/ym4ywb2ezs8uv"}, + {"[Drift] Phonk Radio #5", "https://stream.zeno.fm/aeoju66zrnfuv"}, + {"[Drift] Phonk Radio #6", "https://stream.zeno.fm/71ntub27u18uv"}, + {"[Dubstep] Dub96", "https://radiorecord.hostingradio.ru/dub96.aacp"}, + {"[Random] 105.3 Easy POP and 90s", "http://157.90.133.87:8076/stream"}, + {"[House] True House Chill", "http://stream.truehouse.net:8000/chill"}, + {"[RnB Hits] PowerHitz", "http://66.85.88.174/realrnb"}, + {"[ROCK] BAYERN Radio - 90er Rock", "http://stream.antenne.de:80/90er-rock"}, + {"[ROCK] BAYERN Radio - Classic Rock", "http://stream.antenne.de:80/classic-rock-live"}, + {"[ROCK] 1.FM Classic Rock", "http://185.33.21.111:80/crock_64a"}, + {"[PT Radio & International] Antena1", "https://www.antena1.com.br/stream/"}, + {"[PT Radio] AlienWare", "https://sonic.paulatina.co:10995"}, -- http://elhuecodelasalsa.com/ + {"[Random] 100 Hit Radio", "https://radio1.streamserver.link/radio/8010/100hit-aac"}, + {"[Office] Office Hitz", "http://66.85.88.174/officemix"}, + {"[Pop] Antena1 - 94.7fm", "http://5.135.83.159:80/stream2"}, + {"[Blues] XRDS Blues", "http://198.58.106.133:8321/stream"}, + {"[RU] RussianMix", "https://radiorecord.hostingradio.ru/rus96.aacp"}, + {"[Jazz] Bay Smooth Jazz", "http://185.33.21.111:80/smoothjazz_64a"}, + {"[Trance] Amsterdam Trance", "http://185.33.21.111:80/atr_128"}, + {"[Trance] Absolute Trance Euro", "http://185.33.21.112:80/trance_128"}, + {"[Lovesongs] 100Hits Love", "https://194.97.151.139/lovesongs"}, + {"[ARAB] Arab HitsRadio", "https://icecast.omroep.nl/funx-arab-bb-mp3"}, + {"[ARAB] Arabesk FM", "https://yayin.arabeskfm.biz:8042/"}, + {"[90's] Star 90's", "http://listen.181fm.com/181-star90s_128k.mp3"}, + {"[Random] Hitradio OE3", "https://orf-live.ors-shoutcast.at/oe3-q1a.m3u"}, + {"[Jewish Radio] Jewish Music Stream", "https://stream.jewishmusicstream.com:8000/stream"}, + {"[Thailand Radio] COOLFahrenheit93", "http://103.253.132.7:5004"}, + {"[India] Bollywood Hits Radio", "https://stream.zeno.fm/rqqps6cbe3quv"}, + {"[Russia Radio 1] TatarRadiosi", "https://tatarradio.hostingradio.ru/tatarradio320.mp3"}, + {"[Russia Radio 2] Shanson", "https://chanson.hostingradio.ru:8041/chanson256.mp3"}, + {"[Russia Radio 3] EuropaRussia", "https://europarussia.ru:8006/live"}, + {"[Russia Radio 4] Blatnyachok", "http://89.188.115.214:8000"}, + {"[80s] Russian & International 80s", "https://radiorecord.hostingradio.ru/198096.aacp"}, + {"[90s] Russian & International 90s", "https://radiorecord.hostingradio.ru/sd9096.aacp"}, + {"[Top] 1.FM Hits 2000-s", "http://185.33.21.111:80/hits2000_128"}, + {"[2000s] BAYERN Radio - 2000-s", "http://stream.antenne.de:80/2000er-hits"}, + {"[2010s] BAYERN Radio - 2010-s", "http://stream.antenne.de:80/2010er-hits"}, + {"[70s] SomaFM Seventies", "https://ice1.somafm.com/seventies-128-aac"}, + {"[80s] BAYERN Radio - 80ers", "http://stream.antenne.de:80/80er-kulthits"}, + {"[90s] BAYERN Radio - 90ers", "http://stream.antenne.de:80/90er-hits"}, -- Alternative: http://s1-webradio.antenne.de/90er-hits + {"[Top] BAYERN Radio - Top 1000", "http://stream.antenne.de:80/top-1000"}, + {"[Random] BAYERN Radio - Chillout", "http://stream.antenne.de:80/chillout"}, + {"[Random] BAYERN Radio - Relax", "http://stream.antenne.de:80/relax"}, + {"[Random] BAYERN Radio - Lounge", "http://stream.antenne.de:80/lounge"}, + {"[Random] BAYERN Radio - In The Mix", "http://stream.antenne.de:80/in-the-mix"}, + {"[Random] BAYERN Radio - Spring Hits", "http://stream.antenne.de:80/fruehlings-hits"}, + {"[Random] BAYERN Radio - Happy Hits", "http://stream.antenne.de:80/happy-hits"}, + {"[Random] BAYERN Radio - Greatest Hits", "http://stream.antenne.de:80/greatest-hits"}, + {"[Random] BAYERN Radio - Party Hits", "http://stream.antenne.de:80/party-hits"}, + {"[Random] BAYERN Radio - Pop XXL", "http://stream.antenne.de:80/pop-xxl"}, + {"[Dance / RU] RadioRecord", "https://radiorecord.hostingradio.ru/rr_main96.aacp"}, + {"[Deep96] RadioRecord", "https://radiorecord.hostingradio.ru/deep96.aacp"}, + {"[Mix96] RadioRecord", "https://radiorecord.hostingradio.ru/mix96.aacp"}, + {"[Uplift96] RadioRecord", "https://radiorecord.hostingradio.ru/uplift96.aacp"}, + {"[Ambient96] RadioRecord", "https://radiorecord.hostingradio.ru/ambient96.aacp"}, + {"[Darkside96] RadioRecord", "https://radiorecord.hostingradio.ru/darkside96.aacp"}, + {"[Summer96] RadioRecord", "https://radiorecord.hostingradio.ru/summerparty96.aacp"}, + {"[Dance] BAYERN Radio - Dance XXL", "http://stream.antenne.de:80/dance-xxl"}, + {"[Dance] FunX Dance", "https://icecast.omroep.nl/funx-dance-bb-mp3"}, +} \ No newline at end of file diff --git a/[gameplay]/internetradio/gui/CRadioGUI.lua b/[gameplay]/internetradio/gui/CRadioGUI.lua new file mode 100644 index 000000000..41a7b2952 --- /dev/null +++ b/[gameplay]/internetradio/gui/CRadioGUI.lua @@ -0,0 +1,80 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +RADIO_GUI = false + +local function initializeRadioGUI() + if (RADIO_GUI) then + return false + end + + local screenX, screenY = guiGetScreenSize() + local radioWindowSizeX, radioWindowSizeY = 325, 380 + local radioWindowPosX, radioWindowPosY = (screenX - 325)/1.1, (screenY - 380)/1.4 + local allowRemoteSpeakers = getRadioSetting("allowRemoteSpeakers") + + RADIO_GUI = {} + RADIO_GUI["Radio window"] = guiCreateWindow(radioWindowPosX, radioWindowPosY, radioWindowSizeX, radioWindowSizeY, "SPEAKER MUSIC (RADIO/MP3)", false) + RADIO_GUI["Stream URLs gridlist"] = guiCreateGridList(10, 54, 304, 139, false, RADIO_GUI["Radio window"]) + RADIO_GUI["Stream URL edit"] = guiCreateEdit(10, 25, 304, 26, "http://stream.antenne.de:80/80er-kulthits", false, RADIO_GUI["Radio window"]) + RADIO_GUI["Create speaker button"] = guiCreateButton(10, 200, 150, 30, "CREATE SPEAKER", false, RADIO_GUI["Radio window"]) + RADIO_GUI["Destroy speaker button"] = guiCreateButton(162, 200, 150, 30, "DESTROY SPEAKER", false, RADIO_GUI["Radio window"]) + RADIO_GUI["Play/pause button"] = guiCreateButton(10, 235, 150, 30, "Play - Pause", false, RADIO_GUI["Radio window"]) + RADIO_GUI["Close button"] = guiCreateButton(162, 235, 150, 30, "Close", false, RADIO_GUI["Radio window"]) + + RADIO_GUI["Toggle remote speakers checkbox"] = guiCreateCheckBox(15, 345, 180, 17, "Allow other players speakers", allowRemoteSpeakers, false, RADIO_GUI["Radio window"]) + RADIO_GUI["Toggle remote speakers label"] = guiCreateLabel(167, 345, 150, 17, "", false, RADIO_GUI["Radio window"]) + RADIO_GUI["Radio station URL column"] = guiGridListAddColumn(RADIO_GUI["Stream URLs gridlist"], "Radio station", 0.8) + + guiSetVisible(RADIO_GUI["Radio window"], false) + guiEditSetMaxLength(RADIO_GUI["Stream URL edit"], RADIO_STREAM_URL_MAX_LENGTH) + guiWindowSetSizable(RADIO_GUI["Radio window"], false) + guiGridListSetSortingEnabled(RADIO_GUI["Stream URLs gridlist"], false) + + loadRadioStations() + + addEventHandler("onClientGUIClick", RADIO_GUI["Stream URLs gridlist"], onClientGUIClickLoadStationStreamURL, false) + addEventHandler("onClientGUIClick", RADIO_GUI["Create speaker button"], onClientGUIClickCreateSpeaker, false) + addEventHandler("onClientGUIClick", RADIO_GUI["Play/pause button"], onClientGUIClickToggleSpeaker, false) + addEventHandler("onClientGUIClick", RADIO_GUI["Destroy speaker button"], onClientGUIClickDestroySpeaker, false) + addEventHandler("onClientGUIClick", RADIO_GUI["Close button"], onClientGUIClickCloseRadioGUI, false) + addEventHandler("onClientGUIClick", RADIO_GUI["Toggle remote speakers checkbox"], onClientGUIClickToggleRemoteSpeakers, false) + + for commandID = 1, #RADIO_COMMANDS do + local commandName = RADIO_COMMANDS[commandID] + + addCommandHandler(commandName, toggleRadioGUI) + end + + return true +end + +function onClientGUIClickToggleRemoteSpeakers() + local allowRemoteSpeakers = guiCheckBoxGetSelected(source) + + setRadioSetting("allowRemoteSpeakers", allowRemoteSpeakers) + handleAllSpeakers() +end + +function toggleRadioGUI() + initializeRadioGUI() + + local guiState = guiGetVisible(RADIO_GUI["Radio window"]) + local guiNewState = (not guiState) + + guiSetVisible(RADIO_GUI["Radio window"], guiNewState) + showCursor(guiNewState) +end +bindKey(RADIO_TOGGLE_KEY, "down", toggleRadioGUI) + +function onClientResourceStartRadioGUI() + if (not RADIO_SHOW_ON_START) then + return false + end + + toggleRadioGUI() +end +addEventHandler("onClientResourceStart", resourceRoot, onClientResourceStartRadioGUI) \ No newline at end of file diff --git a/[gameplay]/internetradio/handle_radio/CHandleRadio.lua b/[gameplay]/internetradio/handle_radio/CHandleRadio.lua new file mode 100644 index 000000000..0b1d593b2 --- /dev/null +++ b/[gameplay]/internetradio/handle_radio/CHandleRadio.lua @@ -0,0 +1,331 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +local speakerSounds = {} +local playerSpeakers = {} + +local function getStreamURLFromEdit() + local streamURL = guiGetText(RADIO_GUI["Stream URL edit"]) + local validStreamURL, errorCode = verifyRadioStreamURL(streamURL) + + if (not validStreamURL) then + return false, errorCode + end + + return streamURL +end + +local function handleSpeakerOnStreamInOut(speakerElement, toggleOn) + local validElement = isElement(speakerElement) + + if (not validElement) then + return false + end + + local elementType = getElementType(speakerElement) + local objectType = (elementType == "object") + + if (not objectType) then + return false + end + + for playerElement, speakerData in pairs(playerSpeakers) do + local speakerBox = speakerData.speakerBox + local matchingElement = (speakerBox == speakerElement) + + if (matchingElement) then + toggleSpeakerSounds(playerElement, toggleOn) + + return true + end + end + + return false +end + +function loadRadioStations() + for stationID = 1, #RADIO_STATIONS do + local radioStationData = RADIO_STATIONS[stationID] + local radioStation = radioStationData[1] + local radioStationURL = radioStationData[2] + local radioStationRow = guiGridListAddRow(RADIO_GUI["Stream URLs gridlist"]) + + guiGridListSetItemText(RADIO_GUI["Stream URLs gridlist"], radioStationRow, RADIO_GUI["Radio station URL column"], radioStation, false, false) + guiGridListSetItemData(RADIO_GUI["Stream URLs gridlist"], radioStationRow, RADIO_GUI["Radio station URL column"], radioStationURL) + end + + return true +end + +function toggleSpeakerSounds(playerElement, toggleOn) + local speakerSound = speakerSounds[playerElement] + local speakerSoundElement = isElement(speakerSound) + + if (speakerSoundElement) then + destroyElement(speakerSound) + end + + local allowRemoteSpeakers = getRadioSetting("allowRemoteSpeakers") + + if (not allowRemoteSpeakers) then + local remoteSpeaker = (playerElement ~= localPlayer) + + if (remoteSpeaker) then + toggleOn = false + end + end + + if (toggleOn) then + local speakerData = getPlayerSpeakerData(playerElement) + + if (not speakerData) then + return false + end + + local speakerBox = speakerData.speakerBox + local speakerBoxPosX, speakerBoxPosY, speakerBoxPosZ = getElementPosition(speakerBox) + local speakerInterior = getElementInterior(speakerBox) + local speakerDimension = getElementDimension(speakerBox) + local speakerSoundMaxDistance = speakerData.speakerSoundMaxDistance + local speakerStreamURL = speakerData.speakerStreamURL + local speakerNewSound = playSound3D(speakerStreamURL, speakerBoxPosX, speakerBoxPosY, speakerBoxPosZ, false, false) + + if (not speakerNewSound) then + return false + end + + local speakerPaused = speakerData.speakerPaused + + speakerSounds[playerElement] = speakerNewSound + + setElementInterior(speakerNewSound, speakerInterior) + setElementInterior(speakerNewSound, speakerDimension) + + setSoundPaused(speakerNewSound, speakerPaused) + setSoundMaxDistance(speakerNewSound, speakerSoundMaxDistance) + setSoundVolume(speakerNewSound, 1) + attachElements(speakerNewSound, speakerBox) + end + + if (not toggleOn) then + speakerSounds[playerElement] = nil + end + + return true +end + +function onClientGUIClickLoadStationStreamURL() + local selectedRow, selectedColumn = guiGridListGetSelectedItem(source) + local stationStreamURL = guiGridListGetItemData(source, selectedRow, selectedColumn) + + if (not stationStreamURL) then + return false + end + + guiSetText(RADIO_GUI["Stream URL edit"], stationStreamURL) +end + +function onClientGUIClickCreateSpeaker() + local streamURL, errorCode = getStreamURLFromEdit() + + if (not streamURL) then + local textToDisplay = errorCode or "SPEAKER: Invalid URL, please check your input!" + + outputChatBox(textToDisplay, 255, 0, 0) + + return false + end + + local createDelayPassed = getOrSetPlayerDelay(localPlayer, "create_speaker", RADIO_CREATE_SPEAKER_DELAY) + + if (not createDelayPassed) then + return false + end + + triggerServerEvent("onServerCreateSpeaker", localPlayer, streamURL) +end + +function onClientGUIClickToggleSpeaker() + local playerSpeaker = getPlayerSpeakerData(localPlayer) + + if (not playerSpeaker) then + return false + end + + local toggleDelayPassed = getOrSetPlayerDelay(localPlayer, "toggle_speaker", RADIO_TOGGLE_SPEAKER_DELAY) + + if (not toggleDelayPassed) then + return false + end + + triggerServerEvent("onServerToggleSpeaker", localPlayer) +end + +function onClientGUIClickDestroySpeaker() + local playerSpeaker = getPlayerSpeakerData(localPlayer) + + if (not playerSpeaker) then + return false + end + + local destroyDelayPassed = getOrSetPlayerDelay(localPlayer, "destroy_speaker", RADIO_DESTROY_SPEAKER_DELAY) + + if (not destroyDelayPassed) then + return false + end + + triggerServerEvent("onServerDestroySpeaker", localPlayer) +end + +function onClientGUIClickCloseRadioGUI() + toggleRadioGUI() +end + +function setPlayerSpeakerData(playerElement, speakerData) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local speakerBox = speakerData.speakerBox + local speakerDummy = createObject(1337, 0, 0, 3) + + speakerData.speakerDummy = speakerDummy + playerSpeakers[playerElement] = speakerData + + toggleSpeakerSounds(playerElement, true) + + setElementAlpha(speakerDummy, 0) + setElementCollisionsEnabled(speakerDummy, false) + attachElements(speakerDummy, speakerBox, -0.32, -0.22, 0.8) + + return true +end + +function setPlayerSpeakerPaused(playerElement, pauseState) + local playerSpeakerData = getPlayerSpeakerData(playerElement) + + if (not playerSpeakerData) then + return false + end + + local speakerSound = speakerSounds[playerElement] + + playerSpeakerData.speakerPaused = pauseState + + if (speakerSound) then + local speakerPaused = isSoundPaused(speakerSound) + local updatePauseState = (speakerPaused ~= pauseState) + + if (updatePauseState) then + setSoundPaused(speakerSound, pauseState) + end + end + + return true +end + +function getPlayerSpeakerData(playerElement) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local playerSpeakerData = playerSpeakers[playerElement] + + return playerSpeakerData +end + +function clearPlayerSpeaker(playerOrSpeaker) + for playerElement, speakerData in pairs(playerSpeakers) do + local speakerBox = speakerData.speakerBox + local matchingElement = (playerElement == playerOrSpeaker) or (speakerBox == playerOrSpeaker) + + if (matchingElement) then + local speakerDummy = speakerData.speakerDummy + local speakerDummyElement = isElement(speakerDummy) + + NEARBY_SPEAKERS[speakerDummy] = nil + + if (speakerDummyElement) then + destroyElement(speakerDummy) + end + + toggleSpeakerSounds(playerElement, false) + playerSpeakers[playerElement] = nil + + return true + end + end + + return false +end + +function isObjectSpeaker(objectElement) + for playerElement, speakerData in pairs(playerSpeakers) do + local speakerBox = speakerData.speakerBox + local matchingElement = (speakerBox == objectElement) + + if (matchingElement) then + local speakerSound = speakerSounds[playerElement] + local speakerDummy = speakerData.speakerDummy + + return true, speakerSound, speakerDummy + end + end + + return false +end + +function handleAllSpeakers() + for playerElement, speakerData in pairs(playerSpeakers) do + local speakerBox = speakerData.speakerBox + local speakerBoxStreamedIn = isElementStreamedIn(speakerBox) + + if (speakerBoxStreamedIn) then + toggleSpeakerSounds(playerElement, true) + end + end + + return true +end + +function onClientSyncSpeakers(activeSpeakers) + playerSpeakers = activeSpeakers + handleAllSpeakers() +end +addEvent("onClientSyncSpeakers", true) +addEventHandler("onClientSyncSpeakers", root, onClientSyncSpeakers) + +function onClientCreateSpeaker(speakerData) + setPlayerSpeakerData(source, speakerData) +end +addEvent("onClientCreateSpeaker", true) +addEventHandler("onClientCreateSpeaker", root, onClientCreateSpeaker) + +function onClientToggleSpeaker(pauseState) + setPlayerSpeakerPaused(source, pauseState) +end +addEvent("onClientToggleSpeaker", true) +addEventHandler("onClientToggleSpeaker", root, onClientToggleSpeaker) + +function toggleSpeakerOnStreamIn() + handleSpeakerOnStreamInOut(source, true) +end +addEventHandler("onClientElementStreamIn", resourceRoot, toggleSpeakerOnStreamIn) + +function toggleSpeakerOnStreamOut() + handleSpeakerOnStreamInOut(source, false) +end +addEventHandler("onClientElementStreamOut", resourceRoot, toggleSpeakerOnStreamOut) + +function clearSpeakersOnDestroyQuit() + clearPlayerSpeaker(source) +end +addEventHandler("onClientPlayerQuit", root, clearSpeakersOnDestroyQuit) +addEventHandler("onClientElementDestroy", resourceRoot, clearSpeakersOnDestroyQuit) \ No newline at end of file diff --git a/[gameplay]/internetradio/handle_radio/SHandleRadio.lua b/[gameplay]/internetradio/handle_radio/SHandleRadio.lua new file mode 100644 index 000000000..5bb618fe4 --- /dev/null +++ b/[gameplay]/internetradio/handle_radio/SHandleRadio.lua @@ -0,0 +1,170 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +local playerSpeakers = {} + +function setPlayerSpeakerData(playerElement, speakerData) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + playerSpeakers[playerElement] = speakerData + triggerClientEvent(root, "onClientCreateSpeaker", playerElement, speakerData) + + return true +end + +function getPlayerSpeakerData(playerElement) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local playerSpeakerData = playerSpeakers[playerElement] + + return playerSpeakerData +end + +function clearPlayerSpeaker(playerOrSpeaker) + for playerElement, speakerData in pairs(playerSpeakers) do + local speakerBox = speakerData.speakerBox + local matchingElement = (playerElement == playerOrSpeaker) or (speakerBox == playerOrSpeaker) + + if (matchingElement) then + playerSpeakers[playerElement] = nil + + return true + end + end + + return false +end + +function onServerCreateSpeaker(streamURL) + if (not client) then + return false + end + + local createDelayPassed = getOrSetPlayerDelay(client, "create_speaker", RADIO_CREATE_SPEAKER_DELAY) + + if (not createDelayPassed) then + return false + end + + local validStreamURL = verifyRadioStreamURL(streamURL) + + if (not validStreamURL) then + return false + end + + local playerSpeakerData = getPlayerSpeakerData(client) + + if (playerSpeakerData) then + local speakerBox = playerSpeakerData.speakerBox + local speakerElement = isElement(speakerBox) + + if (speakerElement) then + destroyElement(speakerBox) + end + end + + local playerPosX, playerPosY, playerPosZ = getElementPosition(client) + local playerInterior = getElementInterior(client) + local playerDimension = getElementDimension(client) + + local boxPosX, boxPosY, boxPosZ = (playerPosX - 0.5), (playerPosY + 0.5), (playerPosZ - 1) + local boxRotX, boxRotY, boxRotZ = 0, 0, 0 + local boxLowLOD = false + local boxElement = createObject(RADIO_BOX_MODEL, boxPosX, boxPosY, boxPosZ, boxRotX, boxRotY, boxRotZ, boxLowLOD) + + setElementInterior(boxElement, playerInterior) + setElementDimension(boxElement, playerDimension) + setElementCollisionsEnabled(boxElement, false) + + local speakerData = { + speakerBox = boxElement, + speakerStreamURL = streamURL, + speakerSoundMaxDistance = RADIO_MAX_SOUND_DISTANCE, + speakerPaused = false, + } + + setPlayerSpeakerData(client, speakerData) +end +addEvent("onServerCreateSpeaker", true) +addEventHandler("onServerCreateSpeaker", root, onServerCreateSpeaker) + +function onServerToggleSpeaker() + if (not client) then + return false + end + + local toggleDelayPassed = getOrSetPlayerDelay(client, "toggle_speaker", RADIO_TOGGLE_SPEAKER_DELAY) + + if (not toggleDelayPassed) then + return false + end + + local playerSpeakerData = getPlayerSpeakerData(client) + + if (not playerSpeakerData) then + return false + end + + local speakerPaused = playerSpeakerData.speakerPaused + local pauseNewState = (not speakerPaused) + + playerSpeakerData.speakerPaused = pauseNewState + triggerClientEvent(root, "onClientToggleSpeaker", client, pauseNewState) +end +addEvent("onServerToggleSpeaker", true) +addEventHandler("onServerToggleSpeaker", root, onServerToggleSpeaker) + +function onServerDestroySpeaker() + if (not client) then + return false + end + + local destroyDelayPassed = getOrSetPlayerDelay(client, "destroy_speaker", RADIO_DESTROY_SPEAKER_DELAY) + + if (not destroyDelayPassed) then + return false + end + + local playerSpeakerData = getPlayerSpeakerData(client) + + if (not playerSpeakerData) then + return false + end + + local speakerBox = playerSpeakerData.speakerBox + local speakerElement = isElement(speakerBox) + + if (speakerElement) then + destroyElement(speakerBox) + end +end +addEvent("onServerDestroySpeaker", true) +addEventHandler("onServerDestroySpeaker", root, onServerDestroySpeaker) + +function syncSpeakers(startedResource) + local matchingResource = (startedResource == resource) + + if (not matchingResource) then + return false + end + + triggerClientEvent(source, "onClientSyncSpeakers", source, playerSpeakers) +end +addEventHandler("onPlayerResourceStart", root, syncSpeakers) + +function clearSpeakersOnDestroyQuit() + clearPlayerSpeaker(source) +end +addEventHandler("onPlayerQuit", root, clearSpeakersOnDestroyQuit) +addEventHandler("onElementDestroy", resourceRoot, clearSpeakersOnDestroyQuit) \ No newline at end of file diff --git a/[gameplay]/internetradio/handle_radio/ShHandleRadio.lua b/[gameplay]/internetradio/handle_radio/ShHandleRadio.lua new file mode 100644 index 000000000..4bfbba092 --- /dev/null +++ b/[gameplay]/internetradio/handle_radio/ShHandleRadio.lua @@ -0,0 +1,95 @@ +-- ####################################### +-- ## Project: Internet radio ## +-- ## Authors: MTA contributors ## +-- ## Version: 1.0 ## +-- ####################################### + +local isServer = (not triggerServerEvent) +local playerDelays = {} + +function verifyRadioStreamURL(streamURL) + local urlType = type(streamURL) + local urlString = (urlType == "string") + + if (not urlString) then + return false + end + + local urlLength = utf8.len(streamURL) + local urlValidLength = (urlLength <= RADIO_STREAM_URL_MAX_LENGTH) + + if (not urlValidLength) then + return false + end + + local urlHttp = utf8.find(streamURL, "http") + + if (not urlHttp) then + return false + end + + if (not RADIO_ALLOW_CUSTOM_URLS) then + local allowedStreamURL = false + + for stationID = 1, #RADIO_STATIONS do + local stationData = RADIO_STATIONS[stationID] + local stationStreamURL = stationData[2] + local matchingStreamURL = (stationStreamURL == streamURL) + + if (matchingStreamURL) then + allowedStreamURL = true + break + end + end + + if (not allowedStreamURL) then + return false, "SPEAKER: Custom URLs are not allowed." + end + end + + return true +end + +function getOrSetPlayerDelay(playerElement, delayID, delayTime) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local elementType = getElementType(playerElement) + local playerType = (elementType == "player") + + if (not playerType) then + return false + end + + local playerDelayData = playerDelays[playerElement] + + if (not playerDelayData) then + playerDelays[playerElement] = {} + playerDelayData = playerDelays[playerElement] + end + + local activeDelay = playerDelayData[delayID] + local timeNow = getTickCount() + + if (activeDelay) then + local delayPassed = (timeNow > activeDelay) + + if (not delayPassed) then + return false + end + end + + local delayEndTime = (timeNow + delayTime) + + playerDelayData[delayID] = delayEndTime + + return true +end + +function clearPlayersDelay() + playerDelays[source] = nil +end +addEventHandler(isServer and "onPlayerQuit" or "onClientPlayerQuit", root, clearPlayersDelay) \ No newline at end of file diff --git a/[gameplay]/internetradio/meta.xml b/[gameplay]/internetradio/meta.xml index b48c6b30a..8ada107c7 100644 --- a/[gameplay]/internetradio/meta.xml +++ b/[gameplay]/internetradio/meta.xml @@ -1,6 +1,8 @@ - - -