Files
Bubberstation/code/modules/goonchat/browserOutput.dm
JJRcop 8748e6d83e Internet midi respects start and end times (#43021)
* Play Internet Sound respects start time
For example, adding &t=400 to youtube links.
Doesn't try to parse the start time, youtube-dl does that for us already. Will probably work on most sites not just youtube.

* [Play Internet Sound] now supports end time too

Switched event handler for start to loadeddata from canplay

loadeddata fires when the first frame is available
this is quicker and more consistent than canplay, which tries
to predict at least a few moments of playability being
available before it fires

* [Internet Sound] music_extra_data now starts null
2019-03-08 10:51:04 -05:00

247 lines
7.7 KiB
Plaintext

/*********************************
For the main html chat area
*********************************/
//Precaching a bunch of shit
GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of icons for the browser output
//On client, created on login
/datum/chatOutput
var/client/owner //client ref
var/loaded = FALSE // Has the client loaded the browser output area?
var/list/messageQueue //If they haven't loaded chat, this is where messages will go until they do
var/cookieSent = FALSE // Has the client sent a cookie for analysis
var/broken = FALSE
var/list/connectionHistory //Contains the connection history passed from chat cookie
var/adminMusicVolume = 25 //This is for the Play Global Sound verb
/datum/chatOutput/New(client/C)
owner = C
messageQueue = list()
connectionHistory = list()
/datum/chatOutput/proc/start()
//Check for existing chat
if(!owner)
return FALSE
if(!winexists(owner, "browseroutput")) // Oh goddamnit.
set waitfor = FALSE
broken = TRUE
message_admins("Couldn't start chat for [key_name_admin(owner)]!")
. = FALSE
alert(owner.mob, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.")
return
if(winget(owner, "browseroutput", "is-visible") == "true") //Already setup
doneLoading()
else //Not setup
load()
return TRUE
/datum/chatOutput/proc/load()
set waitfor = FALSE
if(!owner)
return
var/datum/asset/stuff = get_asset_datum(/datum/asset/group/goonchat)
stuff.send(owner)
owner << browse(file('code/modules/goonchat/browserassets/html/browserOutput.html'), "window=browseroutput")
/datum/chatOutput/Topic(href, list/href_list)
if(usr.client != owner)
return TRUE
// Build arguments.
// Arguments are in the form "param[paramname]=thing"
var/list/params = list()
for(var/key in href_list)
if(length(key) > 7 && findtext(key, "param")) // 7 is the amount of characters in the basic param key template.
var/param_name = copytext(key, 7, -1)
var/item = href_list[key]
params[param_name] = item
var/data // Data to be sent back to the chat.
switch(href_list["proc"])
if("doneLoading")
data = doneLoading(arglist(params))
if("debug")
data = debug(arglist(params))
if("ping")
data = ping(arglist(params))
if("analyzeClientData")
data = analyzeClientData(arglist(params))
if("setMusicVolume")
data = setMusicVolume(arglist(params))
if(data)
ehjax_send(data = data)
//Called on chat output done-loading by JS.
/datum/chatOutput/proc/doneLoading()
if(loaded)
return
testing("Chat loaded for [owner.ckey]")
loaded = TRUE
showChat()
for(var/message in messageQueue)
// whitespace has already been handled by the original to_chat
to_chat(owner, message, handle_whitespace=FALSE)
messageQueue = null
sendClientData()
//do not convert to to_chat()
SEND_TEXT(owner, "<span class=\"userdanger\">Failed to load fancy chat, reverting to old chat. Certain features won't work.</span>")
/datum/chatOutput/proc/showChat()
winset(owner, "output", "is-visible=false")
winset(owner, "browseroutput", "is-disabled=false;is-visible=true")
/datum/chatOutput/proc/ehjax_send(client/C = owner, window = "browseroutput", data)
if(islist(data))
data = json_encode(data)
C << output("[data]", "[window]:ehjaxCallback")
/datum/chatOutput/proc/sendMusic(music, list/extra_data)
if(!findtext(music, GLOB.is_http_protocol))
return
var/list/music_data = list("adminMusic" = url_encode(url_encode(music)))
if(extra_data?.len)
music_data["musicRate"] = extra_data["pitch"]
music_data["musicSeek"] = extra_data["start"]
music_data["musicHalt"] = extra_data["end"]
ehjax_send(data = music_data)
/datum/chatOutput/proc/stopMusic()
ehjax_send(data = "stopMusic")
/datum/chatOutput/proc/setMusicVolume(volume = "")
if(volume)
adminMusicVolume = CLAMP(text2num(volume), 0, 100)
//Sends client connection details to the chat to handle and save
/datum/chatOutput/proc/sendClientData()
//Get dem deets
var/list/deets = list("clientData" = list())
deets["clientData"]["ckey"] = owner.ckey
deets["clientData"]["ip"] = owner.address
deets["clientData"]["compid"] = owner.computer_id
var/data = json_encode(deets)
ehjax_send(data = data)
//Called by client, sent data to investigate (cookie history so far)
/datum/chatOutput/proc/analyzeClientData(cookie = "")
if(!cookie)
return
if(cookie != "none")
var/list/connData = json_decode(cookie)
if (connData && islist(connData) && connData.len > 0 && connData["connData"])
connectionHistory = connData["connData"] //lol fuck
var/list/found = new()
for(var/i in connectionHistory.len to 1 step -1)
var/list/row = src.connectionHistory[i]
if (!row || row.len < 3 || (!row["ckey"] || !row["compid"] || !row["ip"])) //Passed malformed history object
return
if (world.IsBanned(row["ckey"], row["compid"], row["ip"], real_bans_only=TRUE))
found = row
break
//Uh oh this fucker has a history of playing on a banned account!!
if (found.len > 0)
//TODO: add a new evasion ban for the CURRENT client details, using the matched row details
message_admins("[key_name(src.owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
log_admin_private("[key_name(owner)] has a cookie from a banned account! (Matched: [found["ckey"]], [found["ip"]], [found["compid"]])")
cookieSent = TRUE
//Called by js client every 60 seconds
/datum/chatOutput/proc/ping()
return "pong"
//Called by js client on js error
/datum/chatOutput/proc/debug(error)
log_world("\[[time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")]\] Client: [(src.owner.key ? src.owner.key : src.owner)] triggered JS error: [error]")
//Global chat procs
/proc/to_chat(target, message, handle_whitespace=TRUE)
if(!target)
return
//Ok so I did my best but I accept that some calls to this will be for shit like sound and images
//It stands that we PROBABLY don't want to output those to the browser output so just handle them here
if (istype(target, /savefile))
CRASH("Invalid message! [message]")
if(!istext(message))
if (istype(message, /image) || istype(message, /sound))
CRASH("Invalid message! [message]")
return
if(target == world)
target = GLOB.clients
var/original_message = message
//Some macros remain in the string even after parsing and fuck up the eventual output
message = replacetext(message, "\improper", "")
message = replacetext(message, "\proper", "")
if(handle_whitespace)
message = replacetext(message, "\n", "<br>")
message = replacetext(message, "\t", "[GLOB.TAB][GLOB.TAB]")
if(islist(target))
// Do the double-encoding outside the loop to save nanoseconds
var/twiceEncoded = url_encode(url_encode(message))
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
if (!C)
continue
//Send it to the old style output window.
SEND_TEXT(C, original_message)
if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
continue
if(!C.chatOutput.loaded)
//Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
continue
C << output(twiceEncoded, "browseroutput:output")
else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
if (!C)
return
//Send it to the old style output window.
SEND_TEXT(C, original_message)
if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
return
if(!C.chatOutput.loaded)
//Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
return
// url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
C << output(url_encode(url_encode(message)), "browseroutput:output")