Skip to content

Commit

Permalink
Allow playing uploaded sounds through the music player w/ Webroot (#4934
Browse files Browse the repository at this point in the history
)

# About the pull request

<!-- Remove this text and explain what the purpose of your PR is.

Mention if you have tested your changes. If you changed a map, make sure
you used the mapmerge tool.
If this is an Issue Correction, you can type "Fixes Issue #169420" to
link the PR to the corresponding Issue number #169420.

Remember: something that is self-evident to you might not be to others.
Explain your rationale fully, even if you feel it goes without saying.
-->

Too long have we suffered at the hands of admin auditive abuse. 

The prophet, spookydonut, once said, "You shouldn't be using this lol".
And he was right. Using "Play MIDI sound" both reduces usability for our
users, and can cause performance issues by freezing up the game for a
while as the data is transfered to these 200 poor CM addicts.

So we sought to alienate it with "Play Internet Sound" backed by
youtube-dl. Unfortunately, some things are subject to geo blocking or
simply not available on Youtube. Thus the regime of terror of Admins
continues.

This PR brings us one step closer to our goal: it allows to use the now
renamed "Play Admin Sound" to (also) upload a sound file to Webroot and
have it played through CDN. It also works with simple transport but that
mostly defeats the point.

Also reduced default volume for new players from 50% to 20%... Don't
worry, It's still way more than enough to get them to quit the server, i
have mine at 2-10% max

# Explain why it's good for the game
* Less new player abuse by reducing default volume
 * More performance by allowing big or custom songs to be backed by CDN
* Better UX: People can easily see the song name and more easily stop it
* Admins can now hide the name of played songs if they want to. Don't
ask me why.

# Testing Photographs and Procedure

![image](https://github.com/cmss13-devs/cmss13/assets/604624/4f00c45d-76ca-47e2-860a-2f26d55de2a4)
You'll have to believe me on the sound working

# Changelog
:cl:
balance: Default Web Music Player volume is now 20% down from 50%. It
was found too effective against new players.
admin: "Play Internet Sound" is now "Play Admin Sound" and optionally
allow to hide the track name.
admin: "Play Admin Sound" can now be used with uploaded tracks, which
use CDN delivery and the in-chat music player, granting players more
control over them.
admin: Removed "Play Midi Sound". 
/:cl:
  • Loading branch information
fira authored Nov 22, 2023
1 parent 643c05d commit 15086ae
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 169 deletions.
2 changes: 1 addition & 1 deletion code/__DEFINES/__game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ block( \
#define SOUND_MIDI (1<<1)
#define SOUND_AMBIENCE (1<<2)
#define SOUND_LOBBY (1<<3)
#define SOUND_INTERNET (1<<4)
#define SOUND_INTERNET (1<<4) // Unused currently. Kept for default prefs compat only
#define SOUND_REBOOT (1<<5)
#define SOUND_ADMIN_MEME (1<<6)
#define SOUND_ADMIN_ATMOSPHERIC (1<<7)
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/sounds.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#define SOUND_CHANNEL_AMBIENCE 1019
#define SOUND_CHANNEL_WALKMAN 1020
#define SOUND_CHANNEL_SOUNDSCAPE 1021
#define SOUND_CHANNEL_ADMIN_MIDI 1022
//#define SOUND_CHANNEL_ADMIN_MIDI 1022
#define SOUND_CHANNEL_LOBBY 1023
#define SOUND_CHANNEL_Z 1024

Expand Down
5 changes: 0 additions & 5 deletions code/datums/soundOutput.dm
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,6 @@
adjust_volume_prefs(VOLUME_AMB, "Set the volume for ambience and soundscapes", 0)
soundOutput.update_ambience(null, null, TRUE)

/client/verb/adjust_volume_admin_music()
set name = "Adjust Volume Admin MIDIs"
set category = "Preferences.Sound"
adjust_volume_prefs(VOLUME_ADM, "Set the volume for admin MIDIs", SOUND_CHANNEL_ADMIN_MIDI)

/client/verb/adjust_volume_lobby_music()
set name = "Adjust Volume LobbyMusic"
set category = "Preferences.Sound"
Expand Down
6 changes: 2 additions & 4 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,8 @@ var/list/admin_verbs_ban = list(
)

var/list/admin_verbs_sounds = list(
/client/proc/play_web_sound,
/client/proc/play_sound,
/client/proc/stop_web_sound,
/client/proc/stop_sound,
/client/proc/play_admin_sound,
/client/proc/stop_admin_sound,
/client/proc/cmd_admin_vox_panel
)

Expand Down
226 changes: 100 additions & 126 deletions code/modules/admin/verbs/playsound.dm
Original file line number Diff line number Diff line change
@@ -1,45 +1,83 @@
/client/proc/play_web_sound()
/client/proc/play_admin_sound()
set category = "Admin.Fun"
set name = "Play Internet Sound"
set name = "Play Admin Sound"
if(!check_rights(R_SOUNDS))
return

var/ytdl = CONFIG_GET(string/invoke_youtubedl)
if(!ytdl)
to_chat(src, SPAN_BOLDWARNING("Youtube-dl was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
var/sound_mode = tgui_input_list(src, "Play a sound from which source?", "Select Source", list("Web", "Upload"))
if(!sound_mode)
return

var/web_sound_input = input("Enter content URL (supported sites only)", "Play Internet Sound via youtube-dl") as text|null
if(!istext(web_sound_input) || !length(web_sound_input))
return
var/list/data = list()
var/log_title = TRUE
var/web_sound_input
var/asset_name
var/must_send_assets = FALSE
var/announce_title = TRUE

if(sound_mode == "Web")
var/ytdl = CONFIG_GET(string/invoke_youtubedl)
if(!ytdl)
to_chat(src, SPAN_BOLDWARNING("Youtube-dl was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
return

web_sound_input = trim(web_sound_input)
web_sound_input = input("Enter content URL (supported sites only)", "Play Internet Sound via youtube-dl") as text|null
if(!istext(web_sound_input) || !length(web_sound_input))
return

if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, SPAN_WARNING("Non-http(s) URIs are not allowed."))
to_chat(src, SPAN_WARNING("For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website."))
return
web_sound_input = trim(web_sound_input)

var/web_sound_url = ""
var/list/music_extra_data = list()
var/title
if(findtext(web_sound_input, ":") && !findtext(web_sound_input, GLOB.is_http_protocol))
to_chat(src, SPAN_WARNING("Non-http(s) URIs are not allowed."))
to_chat(src, SPAN_WARNING("For youtube-dl shortcuts like ytsearch: please use the appropriate full url from the website."))
return

var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_url_scrub(web_sound_input)]\"")
var/errorlevel = output[SHELLEO_ERRORLEVEL]
var/stdout = output[SHELLEO_STDOUT]
var/stderr = output[SHELLEO_STDERR]
var/list/output = world.shelleo("[ytdl] --geo-bypass --format \"bestaudio\[ext=mp3]/best\[ext=mp4]\[height<=360]/bestaudio\[ext=m4a]/bestaudio\[ext=aac]\" --dump-single-json --no-playlist -- \"[shell_url_scrub(web_sound_input)]\"")
var/errorlevel = output[SHELLEO_ERRORLEVEL]
var/stdout = output[SHELLEO_STDOUT]
var/stderr = output[SHELLEO_STDERR]

if(errorlevel)
to_chat(src, SPAN_WARNING("Youtube-dl URL retrieval FAILED: [stderr]"))
return
if(errorlevel)
to_chat(src, SPAN_WARNING("Youtube-dl URL retrieval FAILED: [stderr]"))
return

var/list/data = list()
try
data = json_decode(stdout)
catch(var/exception/e)
to_chat(src, SPAN_WARNING("Youtube-dl JSON parsing FAILED: [e]: [stdout]"))
return
try
data = json_decode(stdout)
catch(var/exception/e)
to_chat(src, SPAN_WARNING("Youtube-dl JSON parsing FAILED: [e]: [stdout]"))
return

else if(sound_mode == "Upload")
var/current_transport = CONFIG_GET(string/asset_transport)
if(!current_transport || current_transport == "simple")
if(tgui_alert(usr, "WARNING: Your server is using simple asset transport. Sounds will have to be sent directly to players, which may freeze the game for long durations. Are you SURE?", "Really play direct sound?", list("Yes", "No")) != "Yes")
return
must_send_assets = TRUE

var/soundfile = input(usr, "Choose a sound file to play", "Upload Sound") as null|file
if(!soundfile)
return

var/static/regex/only_extension = regex(@{"^.*\.([a-z0-9]{1,5})$"}, "gi")
var/extension = only_extension.Replace("[soundfile]", "$1")
if(!length(extension))
to_chat(src, SPAN_WARNING("Invalid filename extension."))
return

var/static/playsound_notch = 1
asset_name = "admin_sound_[playsound_notch++].[extension]"
SSassets.transport.register_asset(asset_name, soundfile)
message_admins("[key_name_admin(src)] uploaded admin sound '[soundfile]' to asset transport.")

var/static/regex/remove_extension = regex(@{"\.[a-z0-9]+$"}, "gi")
data["title"] = remove_extension.Replace("[soundfile]", "")
data["url"] = SSassets.transport.get_asset_url(asset_name)
web_sound_input = "[soundfile]"
log_title = FALSE

var/title
var/web_sound_url = ""
var/list/music_extra_data = list()
if(data["url"])
music_extra_data["link"] = data["url"]
music_extra_data["title"] = data["title"]
Expand All @@ -48,19 +86,28 @@
music_extra_data["start"] = data["start_time"]
music_extra_data["end"] = data["end_time"]

if(web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
if(!must_send_assets && web_sound_url && !findtext(web_sound_url, GLOB.is_http_protocol))
to_chat(src, SPAN_BOLDWARNING("BLOCKED: Content URL not using http(s) protocol"), confidential = TRUE)
to_chat(src, SPAN_WARNING("The media provider returned a content URL that isn't using the HTTP or HTTPS protocol"), confidential = TRUE)
return

switch(tgui_alert(src, "Show the name of this sound to the players?", "Sound Name", list("Yes","No","Cancel")))
if("No")
music_extra_data["title"] = "Admin sound"
announce_title = FALSE
if("Cancel")
return

var/list/targets = list()
var/list/sound_type_list = list(
"Meme" = SOUND_ADMIN_MEME,
"Atmospheric" = SOUND_ADMIN_ATMOSPHERIC
)
var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All Inview", "Single Inview"))

var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All In View Range", "Single Mob"))
var/sound_type = tgui_input_list(src, "What kind of sound is this?", "Select Sound Type", sound_type_list)
sound_type = sound_type_list[sound_type]

switch(style)
if("Globally")
targets = GLOB.mob_list
Expand All @@ -70,30 +117,40 @@
targets = GLOB.human_mob_list + GLOB.dead_mob_list
if("Ghosts")
targets = GLOB.observer_list + GLOB.dead_mob_list
if("All Inview")
targets = viewers(usr.client.view, src)
if("Single Inview")
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", sortmobs())
if("All In View Range")
var/list/atom/ranged_atoms = urange(usr.client.view, get_turf(usr))
for(var/mob/receiver in ranged_atoms)
targets += receiver
if("Single Mob")
var/list/mob/all_mobs = sortmobs()
var/list/mob/all_client_mobs = list()
for(var/mob/mob in all_mobs)
if(mob.client)
all_client_mobs += mob
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", all_client_mobs)
if(QDELETED(choice))
return
targets.Add(choice)
else
return

for(var/i in targets)
var/mob/M = i
var/client/client = M?.client
if((client?.prefs.toggles_sound & SOUND_INTERNET) && (client?.prefs.toggles_sound & sound_type))
for(var/mob/mob as anything in targets)
var/client/client = mob?.client
if((client?.prefs?.toggles_sound & SOUND_MIDI) && (client?.prefs?.toggles_sound & sound_type))
if(must_send_assets)
SSassets.transport.send_assets(client, asset_name)
client?.tgui_panel?.play_music(web_sound_url, music_extra_data)
if(announce_title)
to_chat(client, SPAN_BOLDANNOUNCE("An admin played: [music_extra_data["title"]]"), confidential = TRUE)
else
client?.tgui_panel?.stop_music()

log_admin("[key_name(src)] played web sound: [web_sound_input] - [title] - [style]")
message_admins("[key_name_admin(src)] played web sound: [web_sound_input] - [title] - [style]")
log_admin("[key_name(src)] played admin sound: [web_sound_input] -[log_title ? " [title] -" : ""] [style]")
message_admins("[key_name_admin(src)] played admin sound: [web_sound_input] -[log_title ? " [title] -" : ""] [style]")

/client/proc/stop_web_sound()
/client/proc/stop_admin_sound()
set category = "Admin.Fun"
set name = "Stop Internet Sounds"
set name = "Stop Admin Sounds"

if(!check_rights(R_SOUNDS))
return
Expand All @@ -105,86 +162,3 @@
log_admin("[key_name(src)] stopped the currently playing web sounds.")
message_admins("[key_name_admin(src)] stopped the currently playing web sounds.")

/client/proc/play_sound(S as sound)
set category = "Admin.Fun"
set name = "Play Midi Sound"
if(!check_rights(R_SOUNDS))
return

var/freq = 1
var/vol = tgui_input_number(src, "What volume would you like the sound to play at?", "Volume", 25, 100, 1)
if(!vol)
return
vol = clamp(vol, 1, 100)

var/sound/admin_sound = new()
admin_sound.file = S
admin_sound.priority = 250
admin_sound.channel = SOUND_CHANNEL_ADMIN_MIDI
admin_sound.frequency = freq
admin_sound.wait = 1
admin_sound.repeat = FALSE
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol

var/showtitle = FALSE
var/res = alert(src, "Show the title of this song to the players?",, "Yes","No", "Cancel")
switch(res)
if("Yes")
showtitle = TRUE
if("Cancel")
return

var/list/targets = list()
var/list/sound_type_list = list(
"Meme" = SOUND_ADMIN_MEME,
"Atmospheric" = SOUND_ADMIN_ATMOSPHERIC
)
var/style = tgui_input_list(src, "Who do you want to play this to?", "Select Listeners", list("Globally", "Xenos", "Marines", "Ghosts", "All Inview", "Single Inview"))
var/sound_type = tgui_input_list(src, "What kind of sound is this?", "Select Sound Type", sound_type_list)
sound_type = sound_type_list[sound_type]
switch(style)
if("Globally")
targets = GLOB.mob_list
if("Xenos")
targets = GLOB.xeno_mob_list + GLOB.dead_mob_list
if("Marines")
targets = GLOB.human_mob_list + GLOB.dead_mob_list
if("Ghosts")
targets = GLOB.observer_list + GLOB.dead_mob_list
if("All Inview")
targets = viewers(usr.client.view, src)
if("Single Inview")
var/mob/choice = tgui_input_list(src, "Select the mob to play to:","Select Mob", sortmobs())
if(QDELETED(choice))
return
targets.Add(choice)
else
return

for(var/items in targets)
var/mob/Mob = items
var/client/client = Mob?.client
if((client?.prefs.toggles_sound & SOUND_INTERNET) && (client?.prefs.toggles_sound & sound_type))
admin_sound.volume = vol * client?.admin_music_volume
SEND_SOUND(Mob, admin_sound)
admin_sound.volume = vol
if(showtitle)
to_chat(client, SPAN_BOLDANNOUNCE("An admin played: [S]"), confidential = TRUE)

log_admin("[key_name(src)] played midi sound [S] - [style]")
message_admins("[key_name_admin(src)] played midi sound [S] - [style]")

/client/proc/stop_sound()
set category = "Admin.Fun"
set name = "Stop Midi Sounds"

if(!check_rights(R_SOUNDS))
return

for(var/mob/M in GLOB.player_list)
if(M.client)
SEND_SOUND(M, sound(null))

log_admin("[key_name(src)] stopped midi sounds.")
message_admins("[key_name_admin(src)] stopped midi sounds.")
10 changes: 4 additions & 6 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,7 @@ var/const/MAX_SAVE_SLOTS = 10
dat += "<b>Tooltips:</b> <a href='?_src_=prefs;preference=tooltips'><b>[tooltips ? "Enabled" : "Disabled"]</b></a><br>"
dat += "<b>tgui Window Mode:</b> <a href='?_src_=prefs;preference=tgui_fancy'><b>[(tgui_fancy) ? "Fancy (default)" : "Compatible (slower)"]</b></a><br>"
dat += "<b>tgui Window Placement:</b> <a href='?_src_=prefs;preference=tgui_lock'><b>[(tgui_lock) ? "Primary monitor" : "Free (default)"]</b></a><br>"
dat += "<b>Play Admin Midis:</b> <a href='?_src_=prefs;preference=hear_midis'><b>[(toggles_sound & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Play Admin Internet Sounds:</b> <a href='?_src_=prefs;preference=hear_internet'><b>[(toggles_sound & SOUND_INTERNET) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Play Admin Sounds:</b> <a href='?_src_=prefs;preference=hear_admin_sounds'><b>[(toggles_sound & SOUND_MIDI) ? "Yes" : "No"]</b></a><br>"
dat += "<b>Toggle Meme or Atmospheric Sounds:</b> <a href='?src=\ref[src];action=proccall;procpath=/client/proc/toggle_admin_sound_types'>Toggle</a><br>"
dat += "<b>Set Eye Blur Type:</b> <a href='?src=\ref[src];action=proccall;procpath=/client/proc/set_eye_blur_type'>Set</a><br>"
dat += "<b>Play Lobby Music:</b> <a href='?_src_=prefs;preference=lobby_music'><b>[(toggles_sound & SOUND_LOBBY) ? "Yes" : "No"]</b></a><br>"
Expand Down Expand Up @@ -1807,11 +1806,10 @@ var/const/MAX_SAVE_SLOTS = 10
if("rand_body")
be_random_body = !be_random_body

if("hear_midis")
if("hear_admin_sounds")
toggles_sound ^= SOUND_MIDI

if("hear_internet")
toggles_sound ^= SOUND_INTERNET
if(!(toggles_sound & SOUND_MIDI))
user?.client?.tgui_panel?.stop_music()

if("lobby_music")
toggles_sound ^= SOUND_LOBBY
Expand Down
21 changes: 3 additions & 18 deletions code/modules/client/preferences_toggles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,10 @@
to_chat(src, "You will [(prefs.toggles_sound & SOUND_REBOOT) ? "now" : "no longer"] hear server reboot sounds.")

/client/verb/togglemidis()
set name = "Silence Current Midi"
set name = "Silence Current Admin Sound"
set category = "Preferences.Sound"
set desc = "Toggles hearing sounds uploaded by admins"
// prefs.toggles_sound ^= SOUND_MIDI // Toggle on/off
// prefs.save_preferences() // We won't save the change - it'll be a temporary switch instead of permanent, but they can still make it permanent in character setup.
if(prefs.toggles_sound & SOUND_MIDI) // Not using && midi_playing here - since we can't tell how long an admin midi is, the user should always be able to turn it off at any time.
to_chat(src, SPAN_BOLDNOTICE("The currently playing midi has been silenced."))
var/sound/break_sound = sound(null, repeat = 0, wait = 0, channel = SOUND_CHANNEL_ADMIN_MIDI)
break_sound.priority = 250
src << break_sound //breaks the client's sound output on SOUND_CHANNEL_ADMIN_MIDI
if(src.mob.client.midi_silenced) return
if(midi_playing)
total_silenced++
message_admins("A player has silenced the currently playing midi. Total: [total_silenced] player(s).", 1)
src.mob.client.midi_silenced = 1
spawn(30 SECONDS) // Prevents message_admins() spam. Should match with the midi_playing_timer spawn() in playsound.dm
src.mob.client.midi_silenced = 0
else
to_chat(src, SPAN_BOLDNOTICE("You have 'Play Admin Midis' disabled in your Character Setup, so this verb is useless to you."))
set desc = "Stops the current admin sound. You can also use the STOP icon in the player above tgchat."
tgui_panel?.stop_music()

/client/verb/togglechat()
set name = "Toggle Abovehead Chat"
Expand Down
Loading

0 comments on commit 15086ae

Please sign in to comment.