mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-20 07:02:05 +00:00
So apparently when conditions are right it could make infinite loop (client push -> server receive -> server push -> client receive -> client push...) killing client and server performance. <vui-button> now pushes data as JSON, this preserves data structure of parameters Store now won't allow state to be pushed if it was very recently received There was a plausibly for vueui/Topic() to push data twice, hindering performance. This was mitigated by taking in to consideration object Topic return value if data should pushed. Separated some code in to separate file for future reusal or programical uses.
339 lines
8.6 KiB
Plaintext
339 lines
8.6 KiB
Plaintext
/*
|
|
Byond Vue UI framework
|
|
main ui datum.
|
|
*/
|
|
/datum/vueui
|
|
var/name = "Vueui"
|
|
// title of ui window
|
|
var/title = "HTML5 UI"
|
|
// user that opened this ui
|
|
var/mob/user
|
|
// object that contains this ui
|
|
var/datum/object
|
|
// browser window width
|
|
var/width = 100
|
|
// browser window height
|
|
var/height = 100
|
|
// current state of ui data
|
|
var/list/data
|
|
// currently used server generated assets
|
|
var/list/assets
|
|
// current status of ui
|
|
var/status = STATUS_INTERACTIVE
|
|
var/datum/topic_state/state = null
|
|
// currently active ui component
|
|
var/activeui = "test"
|
|
// header used for this ui, should be set before open()
|
|
var/header = "default"
|
|
// window id
|
|
var/windowid
|
|
// determines if ui state should be constantly be cheacked for updates
|
|
var/auto_update_content = FALSE
|
|
|
|
|
|
/**
|
|
* Creates a new ui
|
|
*
|
|
* @param nuser - user that has opened this ui
|
|
* @param nobject - object that hosts this ui and handles all data / Topic processing
|
|
* @param nactiveui - Vue component that is opened to render this ui's data
|
|
* @param nwidth - initial width of this ui
|
|
* @param nheight - initial height of this ui
|
|
* @param ntitle - title of this ui
|
|
* @param ndata - initial data. Optional.
|
|
* @param nstate - Topic state used for this ui's checks. Optional.
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/New(var/nuser, var/nobject, var/nactiveui = 0, var/nwidth = 0, var/nheight = 0, var/ntitle, var/list/ndata, var/datum/topic_state/nstate = default_state)
|
|
user = nuser
|
|
object = nobject
|
|
data = ndata
|
|
state = nstate
|
|
|
|
if (nactiveui)
|
|
activeui = nactiveui
|
|
if (nwidth)
|
|
width = nwidth
|
|
if (nheight)
|
|
height = nheight
|
|
if (ntitle)
|
|
title = ntitle
|
|
|
|
SSvueui.ui_opened(src)
|
|
windowid = "vueui\ref[src]"
|
|
name = "Vueui [object]/[user]"
|
|
|
|
/**
|
|
* Opens this ui, gathers initial data
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/open()
|
|
if(QDELETED(object))
|
|
return
|
|
if(!user.client)
|
|
return
|
|
|
|
if(!data)
|
|
data = object.vueui_data_change(null, user, src)
|
|
update_status()
|
|
if(!status || status == STATUS_CLOSE)
|
|
return
|
|
|
|
var/params = "window=[windowid];"
|
|
if(width && height)
|
|
params += "size=[width]x[height];"
|
|
send_resources_and_assets(user.client)
|
|
user << browse(generate_html(), params)
|
|
winset(user, "mapwindow.map", "focus=true")
|
|
addtimer(CALLBACK(src, /datum/vueui/proc/setclose), 1)
|
|
|
|
/datum/vueui/proc/setclose()
|
|
winset(user, windowid, "on-close=\"vueuiclose \ref[src]\"")
|
|
|
|
/**
|
|
* Closes this ui
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/close()
|
|
SSvueui.ui_closed(src)
|
|
user << browse(null, "window=[windowid]")
|
|
status = null
|
|
|
|
/**
|
|
* Generates base html for this ui to be rendered.
|
|
*
|
|
* @return html code - text
|
|
*/
|
|
/datum/vueui/proc/generate_html()
|
|
#ifdef UIDEBUG
|
|
var/debugtxt = "<div id=\"dapp\"></div>"
|
|
#else
|
|
var/debugtxt = ""
|
|
#endif
|
|
return {"
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta charset="UTF-8">
|
|
<link rel="stylesheet" type="text/css" href="vueui.css">
|
|
</head>
|
|
<body class="[get_theme_class()]">
|
|
<div id="header">
|
|
<header-[header]></header-[header]>
|
|
</div>
|
|
<div id="app">
|
|
Javascript file has failed to load. <a href="?src=\ref[src]&vueuiforceresource=1">Click here to force load resources</a>
|
|
</div>
|
|
[debugtxt]
|
|
<noscript>
|
|
<div id='uiNoScript'>
|
|
<h2>JAVASCRIPT REQUIRED</h2>
|
|
<p>Your Internet Explorer's Javascript is disabled (or broken).<br/>
|
|
Enable Javascript and then open this UI again.</p>
|
|
</div>
|
|
</noscript>
|
|
</body>
|
|
<script type="application/json" id="initialstate">
|
|
[generate_data_json()]
|
|
</script>
|
|
<script type="text/javascript" src="vueui.js"></script>
|
|
</html>
|
|
"}
|
|
|
|
/**
|
|
* Generates json state object to be sent to ui.
|
|
*
|
|
* @return json object - text
|
|
*/
|
|
/datum/vueui/proc/generate_data_json()
|
|
var/list/sdata = list()
|
|
sdata["state"] = src.data
|
|
sdata["assets"] = list()
|
|
sdata["active"] = activeui
|
|
sdata["uiref"] = "\ref[src]"
|
|
sdata["status"] = status
|
|
sdata["title"] = title
|
|
sdata["wtime"] = world.time
|
|
for(var/asset_name in assets)
|
|
var/asset = assets[asset_name]
|
|
sdata["assets"][asset_name] = list("ref" = ckey("\ref[asset["img"]]"))
|
|
return json_encode(sdata)
|
|
|
|
/**
|
|
* Sends all resources required for proper renderig of ui
|
|
*
|
|
* @param cl - client that should get assets
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/send_resources_and_assets(var/client/cl)
|
|
#ifdef UIDEBUG
|
|
cl << browse_rsc(file("vueui/dist/app.js"), "vueui.js")
|
|
cl << browse_rsc(file("vueui/dist/app.css"), "vueui.css")
|
|
#else
|
|
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/vueui)
|
|
assets.send(cl)
|
|
#endif
|
|
for(var/asset_name in assets)
|
|
var/asset = assets[asset_name]
|
|
if (!QDELETED(asset["img"]))
|
|
cl << browse_rsc(asset["img"], "vueuiimg_" + ckey("\ref[asset["img"]]") + ".png")
|
|
|
|
/**
|
|
* Sends requested asset to ui's client
|
|
*
|
|
* @param name - asset's name that should be sent to client
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/send_asset(var/name)
|
|
if (!QDELETED(user) || !user.client)
|
|
return
|
|
name = ckey(name)
|
|
if (name in assets)
|
|
user.client << browse_rsc(assets[name]["img"], "vueuiimg_" + ckey("\ref[assets[name]["img"]]") + ".png")
|
|
|
|
/**
|
|
* Adds / sets dynamic asset for this ui's use
|
|
*
|
|
* @param name - name of asset that should be added
|
|
* @param img - image, an asset that will be used
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/add_asset(var/name, var/image/img)
|
|
if(QDELETED(img))
|
|
return
|
|
name = ckey(name)
|
|
LAZYINITLIST(assets)
|
|
assets[name] = list("name" = name, "img" = img)
|
|
|
|
/**
|
|
* Removes dynamic asset for this ui's use
|
|
*
|
|
* @param name - name of asset that will be removed
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/remove_asset(var/name)
|
|
name = ckey(name)
|
|
assets -= assets[name]
|
|
|
|
/**
|
|
* Handles interactivity and state updates
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/Topic(href, href_list)
|
|
. = update_status(FALSE)
|
|
if(status < STATUS_INTERACTIVE || user != usr)
|
|
return
|
|
if(href_list["vueuiforceresource"])
|
|
if(user.client)
|
|
user.client << browse_rsc(file("vueui/dist/app.js"), "vueui.js")
|
|
user.client << browse_rsc(file("vueui/dist/app.css"), "vueui.css")
|
|
open()
|
|
if(href_list["vueuistateupdate"])
|
|
var/rdata = json_decode(href_list["vueuistateupdate"])
|
|
var/ndata = rdata["state"]
|
|
var/ret = object.vueui_data_change(ndata, user, src)
|
|
if(ret)
|
|
. = TRUE
|
|
ndata = ret
|
|
src.data = ndata
|
|
var/topicReturn = 0
|
|
if(!href_list["vueuipushonly"])
|
|
if(href_list["vueuihrefjson"])
|
|
var/json_href = json_decode(href_list["vueuihrefjson"])
|
|
if(json_href)
|
|
for(var/hvar in json_href)
|
|
href_list[hvar] = json_href[hvar]
|
|
href_list["vueui"] = src // Let's pass our UI object to object for it to do things.
|
|
topicReturn = object.Topic(href, href_list)
|
|
if(. || topicReturn)
|
|
. = null
|
|
push_change()
|
|
|
|
/**
|
|
* Pushes latest data to client (Including metadata such as: assets index, status, activeui)
|
|
*
|
|
* @param ndate - new data that should be pushed to client, if null then exisitng data is pushed
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/push_change(var/list/ndata)
|
|
if(ndata && status > STATUS_DISABLED)
|
|
src.data = ndata
|
|
user << output(list2params(list(generate_data_json())),"[windowid].browser:receiveUIState")
|
|
|
|
/**
|
|
* Check for change and push that change of data
|
|
*
|
|
* @param force - determines should data be pushed even if no change is present
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/check_for_change(var/force = 0)
|
|
if(status > STATUS_DISABLED)
|
|
var/ret = object.vueui_data_change(data, user, src)
|
|
if(ret)
|
|
push_change(ret)
|
|
else if (force)
|
|
push_change(null)
|
|
else if (force && status == STATUS_DISABLED)
|
|
push_change(null)
|
|
|
|
/**
|
|
* Set the current status (also known as visibility) of this ui.
|
|
*
|
|
* @param state int The status to set, see the defines at the top of this file
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/set_status(var/nstatus, var/autopush = TRUE)
|
|
if (nstatus != status) // Only update if it is different
|
|
status = nstatus
|
|
if(nstatus > STATUS_DISABLED)
|
|
if(autopush) check_for_change(1) // Gather data and update it
|
|
return 1
|
|
else if (nstatus == STATUS_DISABLED && autopush)
|
|
if(autopush) push_change(null) // Only update ui data
|
|
return 1
|
|
else
|
|
close()
|
|
|
|
/**
|
|
* Update the status (visibility) of this ui based on the user's status
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/proc/update_status(var/autopush = TRUE)
|
|
. = set_status(object.CanUseTopic(user, state), autopush)
|
|
|
|
/**
|
|
* Process this ui
|
|
*
|
|
* @return nothing
|
|
*/
|
|
/datum/vueui/process()
|
|
if (!object || !user || status < 0)
|
|
close()
|
|
return
|
|
update_status()
|
|
if(auto_update_content)
|
|
check_for_change()
|
|
|
|
/**
|
|
* Returns actuve theme on varous conditions
|
|
*
|
|
* @return themes class - text
|
|
*/
|
|
/datum/vueui/proc/get_theme_class()
|
|
return SSvueui.get_html_theme_class(user)
|
|
|
|
#undef UIDEBUG
|