mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-21 20:11:04 +01:00
Bun, Inferno->React migration (#22529)
Re-creation of https://github.com/Aurorastation/Aurora.3/pull/21046 to skip merge conflict hell. Brings us modern TGUI. **ALTERNATE TITLE: TGUI HELLSCAPE PR ABANDON ALL HOPE YE WHO ENTER HERE** - [x] Migrate build tools (javascript -> typescript, bun for package management). - [x] Upgrade all TGUI dependencies and associated root files to TG-congruent versions (axios, babel, dompurify, eslint, highlight, marked, prettier, sass, source-map, stacktrace-parser, typescript). - [x] InfernoJS -> React migrations - [x] React cleanup and polish (migrate all remaining .js files to appropriate .ts or .tsx filetype, all remaining hooks, linting, error corrections, etc.) - [ ] Test all remaining TGUI interfaces
This commit is contained in:
@@ -180,7 +180,6 @@ GLOBAL_LIST_INIT(admin_verbs_server, list(
|
||||
/datum/admins/proc/toggle_round_spookyness,
|
||||
/datum/admins/proc/toggle_space_ninja,
|
||||
/client/proc/toggle_random_events,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/toggle_recursive_explosions,
|
||||
/client/proc/restart_controller,
|
||||
/client/proc/cmd_ss_panic,
|
||||
@@ -355,7 +354,6 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/datum/admins/proc/adjump,
|
||||
/datum/admins/proc/toggle_space_ninja,
|
||||
/client/proc/toggle_random_events,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/play_local_sound,
|
||||
/client/proc/play_sound,
|
||||
/client/proc/play_server_sound,
|
||||
@@ -475,7 +473,6 @@ GLOBAL_LIST_INIT(admin_verbs_dev, list( //will need to be altered - Ryan784
|
||||
/client/proc/jumptoship,
|
||||
/client/proc/jumptosector,
|
||||
/client/proc/cmd_dev_say,
|
||||
/client/proc/nanomapgen_DumpImage,
|
||||
/client/proc/admin_ghost,
|
||||
/client/proc/air_report,
|
||||
/client/proc/enable_debug_verbs,
|
||||
|
||||
@@ -463,21 +463,6 @@
|
||||
|
||||
usr << browse(HTML_SKELETON(replacetext(SSatoms.InitLog(), "\n", "<br>")), "window=initlog")
|
||||
|
||||
/client/proc/reload_nanoui_resources()
|
||||
set category = "Debug"
|
||||
set name = "Reload NanoUI Resources"
|
||||
set desc = "Force the client to redownload NanoUI Resources"
|
||||
|
||||
// Close open NanoUIs.
|
||||
SSnanoui.close_user_uis(usr)
|
||||
|
||||
// Re-load the assets.
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
|
||||
assets.register()
|
||||
|
||||
// Clear the user's cache so they get resent.
|
||||
usr.client.sent_assets = list()
|
||||
|
||||
/**
|
||||
* Used to generate lag and load the MC to test how things work under live server stress
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
S["UI_style"] >> pref.UI_style
|
||||
S["UI_style_color"] >> pref.UI_style_color
|
||||
S["UI_style_alpha"] >> pref.UI_style_alpha
|
||||
S["tgui_fancy"] >> pref.tgui_fancy
|
||||
S["tgui_lock"] >> pref.tgui_lock
|
||||
S["ooccolor"] >> pref.ooccolor
|
||||
S["clientfps"] >> pref.clientfps
|
||||
@@ -21,7 +20,6 @@
|
||||
S["UI_style"] << pref.UI_style
|
||||
S["UI_style_color"] << pref.UI_style_color
|
||||
S["UI_style_alpha"] << pref.UI_style_alpha
|
||||
S["tgui_fancy"] << pref.tgui_fancy
|
||||
S["tgui_lock"] << pref.tgui_lock
|
||||
S["ooccolor"] << pref.ooccolor
|
||||
S["clientfps"] << pref.clientfps
|
||||
@@ -39,7 +37,6 @@
|
||||
"UI_style",
|
||||
"UI_style_color",
|
||||
"UI_style_alpha",
|
||||
"tgui_fancy",
|
||||
"tgui_lock",
|
||||
"ooccolor",
|
||||
"clientfps",
|
||||
@@ -63,7 +60,6 @@
|
||||
"UI_style",
|
||||
"UI_style_color",
|
||||
"UI_style_alpha",
|
||||
"tgui_fancy",
|
||||
"tgui_lock",
|
||||
"ooccolor",
|
||||
"clientfps",
|
||||
@@ -83,7 +79,6 @@
|
||||
"UI_style_alpha" = pref.UI_style_alpha,
|
||||
"UI_style_color" = pref.UI_style_color,
|
||||
"UI_style" = pref.UI_style,
|
||||
"tgui_fancy" = pref.tgui_fancy,
|
||||
"tgui_lock" = pref.tgui_lock,
|
||||
"ooccolor" = pref.ooccolor,
|
||||
"clientfps" = pref.clientfps,
|
||||
@@ -100,7 +95,6 @@
|
||||
pref.UI_style_color = sanitize_hexcolor(pref.UI_style_color, initial(pref.UI_style_color))
|
||||
pref.UI_style_alpha = sanitize_integer(text2num(pref.UI_style_alpha), 0, 255, initial(pref.UI_style_alpha))
|
||||
pref.clientfps = sanitize_integer(text2num(pref.clientfps), 0, 1000, initial(pref.clientfps))
|
||||
pref.tgui_fancy = sanitize_bool(pref.tgui_fancy, TRUE)
|
||||
pref.tgui_lock = sanitize_bool(pref.tgui_lock, FALSE)
|
||||
pref.tgui_inputs = sanitize_bool(pref.tgui_inputs, TRUE)
|
||||
pref.tgui_buttons_large = sanitize_bool(pref.tgui_buttons_large, FALSE)
|
||||
@@ -116,7 +110,6 @@
|
||||
dat += "-Color: <a href='byond://?src=[REF(src)];select_color=1'><b>[pref.UI_style_color]</b></a> [HTML_RECT(pref.UI_style_color)] - <a href='byond://?src=[REF(src)];reset=ui'>reset</a><br>"
|
||||
dat += "-Alpha(transparency): <a href='byond://?src=[REF(src)];select_alpha=1'><b>[pref.UI_style_alpha]</b></a> - <a href='byond://?src=[REF(src)];reset=alpha'>reset</a><br>"
|
||||
dat += "<b>Tooltip Style:</b> <a href='byond://?src=[REF(src)];select_tooltip_style=1'><b>[pref.tooltip_style]</b></a><br>"
|
||||
dat += "<b>TGUI Fancy:</b> <a href='byond://?src=[REF(src)];select_tguif=1'><b>[pref.tgui_fancy ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Lock:</b> <a href='byond://?src=[REF(src)];select_tguil=1'><b>[pref.tgui_lock ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Inputs:</b> <a href='byond://?src=[REF(src)];tgui_inputs=1'><b>[pref.tgui_inputs ? "ON" : "OFF"]</b></a><br>"
|
||||
dat += "<b>TGUI Input Large Buttons:</b> <a href='byond://?src=[REF(src)];tgui_inputs_large=1'><b>[pref.tgui_buttons_large ? "ON" : "OFF"]</b></a><br>"
|
||||
@@ -152,10 +145,6 @@
|
||||
pref.UI_style_alpha = UI_style_alpha_new
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["select_tguif"])
|
||||
pref.tgui_fancy = !pref.tgui_fancy
|
||||
return TOPIC_REFRESH
|
||||
|
||||
else if(href_list["select_tguil"])
|
||||
pref.tgui_lock = !pref.tgui_lock
|
||||
return TOPIC_REFRESH
|
||||
|
||||
@@ -26,7 +26,6 @@ GLOBAL_LIST_EMPTY_TYPED(preferences_datums, /datum/preferences)
|
||||
var/sfx_toggles = ASFX_DEFAULT
|
||||
var/UI_style_color = "#ffffff"
|
||||
var/UI_style_alpha = 255
|
||||
var/tgui_fancy = TRUE
|
||||
var/tgui_lock = FALSE
|
||||
var/tgui_inputs = TRUE
|
||||
var/tgui_buttons_large = FALSE
|
||||
|
||||
@@ -77,6 +77,16 @@
|
||||
QDEL_LIST(accessories)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/examine_descriptor(mob/user)
|
||||
return "clothing"
|
||||
|
||||
/obj/item/clothing/examine_tags(mob/user)
|
||||
. = ..()
|
||||
if(item_flags & ITEM_FLAG_THICK_MATERIAL)
|
||||
.["thick"] = "Stops or slows minor piercing effects, such as from injectors."
|
||||
if(item_flags & ITEM_FLAG_INJECTION_PORT)
|
||||
.["port-enabled"] = "Has a dedicated injection port for syringes and hypo-sprays to be used on the wearer."
|
||||
|
||||
//Updates the icons of the mob wearing the clothing item, if any.
|
||||
/obj/item/clothing/proc/update_clothing_icon()
|
||||
return
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
icon_state = "hardhat_yellow"
|
||||
item_state = "hardhat_yellow"
|
||||
light_overlay = "hardhat_light"
|
||||
item_flags = ITEM_FLAG_THICK_MATERIAL
|
||||
contained_sprite = TRUE
|
||||
action_button_name = "Toggle Headlamp"
|
||||
light_range = 4 //luminosity when on
|
||||
|
||||
@@ -325,9 +325,6 @@
|
||||
if(istype(exosuit) && exosuit.head && exosuit.head.radio && exosuit.head.radio.is_functional())
|
||||
return ..()
|
||||
|
||||
/obj/item/radio/exosuit/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/ui_state/state = GLOB.mech_state)
|
||||
. = ..()
|
||||
|
||||
/mob/living/heavy_vehicle/proc/become_remote()
|
||||
for(var/mob/user in pilots)
|
||||
eject(user, FALSE)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
//TGUI
|
||||
remove_all_indicators()
|
||||
|
||||
SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
||||
GLOB.player_list -= src
|
||||
disconnect_time = world.realtime
|
||||
log_access("Logout: [key_name(src)]")
|
||||
|
||||
@@ -45,8 +45,7 @@ if one is destroyed the second will take over. If all relays are gone it stops w
|
||||
possible to enable/disable Software Downloading, P2P file transfers and Communication (IC version of IRC, PDA messages for more than two people)
|
||||
|
||||
5. Software
|
||||
Software would almost exclusively use NanoUI modules. Few exceptions are text editor (uses similar screen as TCS IDE used for editing and classic HTML for previewing as Nano looks differently)
|
||||
and similar programs which for some reason require HTML UI. Most software will be highly dependent on NTNet to work as laptops are not physically connected to the station's network.
|
||||
Software would almost exclusively use TGUI modules. Most software will be highly dependent on NTNet to work as laptops are not physically connected to the station's network.
|
||||
What i plan to add:
|
||||
|
||||
Note: XXXXDB programs will use ingame_manuals to display basic help for players, similar to how books, etc. do
|
||||
|
||||
@@ -416,13 +416,10 @@
|
||||
|
||||
/obj/item/modular_computer/proc/update_uis()
|
||||
if(active_program) //Should we update program ui or computer ui?
|
||||
SSnanoui.update_uis(active_program)
|
||||
SStgui.update_uis(src)
|
||||
if(active_program.NM)
|
||||
SSnanoui.update_uis(active_program.NM)
|
||||
if(active_program)
|
||||
SStgui.update_uis(active_program)
|
||||
else
|
||||
SStgui.update_uis(src)
|
||||
SSnanoui.update_uis(src)
|
||||
|
||||
/obj/item/modular_computer/proc/check_update_ui_need()
|
||||
var/ui_update_needed = FALSE
|
||||
@@ -458,18 +455,6 @@
|
||||
if(ui_update_needed)
|
||||
update_uis()
|
||||
|
||||
// Used by camera monitor program
|
||||
/obj/item/modular_computer/check_eye(var/mob/user)
|
||||
if(active_program)
|
||||
return active_program.check_eye(user)
|
||||
return ..()
|
||||
|
||||
// Used by camera monitor program
|
||||
/obj/item/modular_computer/grants_equipment_vision(var/mob/user)
|
||||
if(active_program)
|
||||
return active_program.grants_equipment_vision(user)
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/get_cell()
|
||||
return battery_module ? battery_module.get_cell() : DEVICE_NO_CELL
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
|
||||
/obj/item/modular_computer/proc/get_header_data(list/data)
|
||||
LAZYINITLIST(data)
|
||||
data["PC_batteryicon"] = get_battery_icon()
|
||||
data["PC_batteryicon"] = battery_module ? get_battery_icon() : null
|
||||
data["PC_showbatteryicon"] = !!battery_module
|
||||
data["PC_batterypercent"] = battery_module ? "[round(battery_module.battery.percent())] %" : "N/C"
|
||||
data["PC_batterypercent"] = battery_module ? "[round(battery_module.battery.percent())] %" : null
|
||||
data["PC_apclinkicon"] = (tesla_link?.enabled && apc_powered) ? "charging.gif" : ""
|
||||
data["PC_device_theme"] = active_program ? active_program.tgui_theme : "scc"
|
||||
data["PC_ntneticon"] = get_ntnet_status_icon()
|
||||
@@ -206,5 +206,15 @@
|
||||
return UI_INTERACTIVE
|
||||
. = ..()
|
||||
if(. < UI_INTERACTIVE)
|
||||
if(user.machine)
|
||||
if(user?.machine)
|
||||
user.unset_machine()
|
||||
|
||||
/obj/item/modular_computer/check_eye(mob/user)
|
||||
if(active_program)
|
||||
return active_program.check_eye(user)
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/grants_equipment_vision(mob/user)
|
||||
if(active_program)
|
||||
return active_program.grants_equipment_vision(user)
|
||||
return ..()
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/obj/item/modular_computer/silicon/ui_host()
|
||||
. = computer_host
|
||||
|
||||
/obj/item/modular_computer/silicon/ui_state(mob/user)
|
||||
return GLOB.self_state
|
||||
|
||||
/obj/item/modular_computer/silicon/Initialize(mapload)
|
||||
if(istype(loc, /mob/living/silicon))
|
||||
computer_host = loc
|
||||
|
||||
@@ -84,11 +84,7 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
var/tgui_theme = "scc"
|
||||
|
||||
/// If this TGUI should autoupdate or not.
|
||||
var/ui_auto_update = TRUE
|
||||
|
||||
// TO BE DEPRECATED:
|
||||
var/datum/nano_module/NM // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact.
|
||||
var/nanomodule_path // Path to nanomodule, make sure to set this if implementing new program.
|
||||
var/ui_auto_update = TRUE // Path to nanomodule, make sure to set this if implementing new program.
|
||||
|
||||
/datum/computer_file/program/New(obj/item/modular_computer/comp)
|
||||
..()
|
||||
@@ -136,7 +132,6 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
var/datum/computer_file/program/temp = ..(rename, computer)
|
||||
temp.required_access_run = required_access_run
|
||||
temp.required_access_download = required_access_download
|
||||
temp.nanomodule_path = nanomodule_path
|
||||
temp.filedesc = filedesc
|
||||
temp.program_icon_state = program_icon_state
|
||||
temp.requires_ntnet = requires_ntnet
|
||||
@@ -200,6 +195,14 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
return computer.get_ntnet_status(specific_action)
|
||||
return FALSE
|
||||
|
||||
/// Allows active programs to preserve non-standard user views, such as camera monitors.
|
||||
/datum/computer_file/program/proc/check_eye(mob/user)
|
||||
return -1
|
||||
|
||||
/// Allows active programs to grant equipment vision while controlling the user's view.
|
||||
/datum/computer_file/program/proc/grants_equipment_vision(mob/user)
|
||||
return FALSE
|
||||
|
||||
// Called by Process() on device that runs us, once every tick.
|
||||
/datum/computer_file/program/proc/process_tick()
|
||||
return TRUE
|
||||
@@ -307,31 +310,12 @@ ABSTRACT_TYPE(/datum/computer_file/program)
|
||||
else // Should never happen - So fail silently
|
||||
return FALSE
|
||||
|
||||
// This attempts to retrieve header data for NanoUIs. If implementing completely new device of different type than existing ones
|
||||
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
|
||||
// always include the device here in this proc. This proc basically relays the request to whatever is running the program.
|
||||
/datum/computer_file/program/proc/get_header_data()
|
||||
if(computer)
|
||||
return computer.get_header_data()
|
||||
|
||||
|
||||
/datum/computer_file/program/Topic(href, href_list)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(computer)
|
||||
return computer.Topic(href, href_list)
|
||||
|
||||
// Relays the call to nano module, if we have one
|
||||
/datum/computer_file/program/proc/check_eye(var/mob/user)
|
||||
if(NM)
|
||||
return NM.check_eye(user)
|
||||
else
|
||||
return -1
|
||||
|
||||
/// Relays the call to nano module, if we have one
|
||||
/datum/computer_file/program/proc/grants_equipment_vision(var/mob/user)
|
||||
if(NM)
|
||||
return NM.grants_equipment_vision(user)
|
||||
|
||||
/datum/computer_file/program/proc/message_dead(var/message)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.stat == DEAD && (M.client && M.client.prefs.toggles & CHAT_GHOSTEARS))
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
// When implementing new program based device, use this to run the program.
|
||||
/datum/computer_file/program/proc/run_program(var/mob/user)
|
||||
if(can_run(user, 1) || !requires_access_to_run)
|
||||
if(nanomodule_path)
|
||||
NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src)
|
||||
if(requires_ntnet && network_destination)
|
||||
generate_network_log("Connection opened to [network_destination].")
|
||||
program_state = PROGRAM_STATE_ACTIVE
|
||||
@@ -18,9 +16,6 @@
|
||||
program_state = PROGRAM_STATE_KILLED
|
||||
if(network_destination)
|
||||
generate_network_log("Connection to [network_destination] closed.")
|
||||
if(NM)
|
||||
qdel(NM)
|
||||
NM = null
|
||||
return TRUE
|
||||
|
||||
// Is called when program service is being activated
|
||||
|
||||
@@ -1,39 +1,5 @@
|
||||
/obj/item/modular_computer/initial_data()
|
||||
/obj/item/modular_computer/proc/initial_data()
|
||||
return list("_PC" = get_header_data())
|
||||
|
||||
/datum/computer_file/program/initial_data()
|
||||
/datum/computer_file/program/proc/initial_data()
|
||||
return list("_PC" = get_header_data())
|
||||
|
||||
/obj/item/modular_computer/update_layout()
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/update_layout()
|
||||
return TRUE
|
||||
|
||||
/datum/nano_module/program
|
||||
available_to_ai = FALSE
|
||||
var/datum/computer_file/program/program // Program-Based computer program that runs this nano module. Defaults to null.
|
||||
|
||||
/datum/nano_module/program/New(var/host, var/topic_manager, var/program)
|
||||
..()
|
||||
src.program = program
|
||||
|
||||
/datum/nano_module/program/Destroy()
|
||||
program = null
|
||||
return ..()
|
||||
|
||||
/datum/topic_manager/program
|
||||
var/datum/program
|
||||
|
||||
/datum/topic_manager/program/New(var/datum/program)
|
||||
..()
|
||||
src.program = program
|
||||
|
||||
/datum/topic_manager/program/Destroy()
|
||||
program = null
|
||||
return ..()
|
||||
|
||||
// Calls forwarded to PROGRAM itself should begin with "PRG_"
|
||||
// Calls forwarded to COMPUTER running the program should begin with "PC_"
|
||||
/datum/topic_manager/program/Topic(href, href_list)
|
||||
return program && program.Topic(href, href_list)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
available_on_ntnet = TRUE
|
||||
usage_flags = PROGRAM_ALL
|
||||
tgui_id = "CargoOrder"
|
||||
ui_auto_update = FALSE
|
||||
|
||||
var/page = "main" //main - Main Menu, order - Order Page, item_details - Item Details Page, tracking - Tracking Page
|
||||
var/selected_category = null // Category that is currently selected
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
size = 8
|
||||
color = LIGHT_COLOR_BLUE
|
||||
tgui_id = "IDCardModification"
|
||||
ui_auto_update = FALSE
|
||||
var/is_centcom = FALSE
|
||||
var/show_assignments = FALSE
|
||||
|
||||
|
||||
+6
-2
@@ -12,7 +12,6 @@
|
||||
network_destination = "station long-range communication array"
|
||||
color = LIGHT_COLOR_BLUE
|
||||
tgui_id = "CommandCommunications"
|
||||
ui_auto_update = FALSE
|
||||
var/datum/comm_message_listener/message_core
|
||||
var/intercept = FALSE
|
||||
var/can_call_shuttle = FALSE //If calling the shuttle should be available from this console
|
||||
@@ -272,7 +271,12 @@ GLOBAL_VAR_INIT(last_message_id, 0)
|
||||
/datum/comm_message_listener/proc/Add(var/list/message)
|
||||
messages[++messages.len] = message
|
||||
|
||||
/datum/comm_message_listener/proc/Remove(var/list/message)
|
||||
/datum/comm_message_listener/proc/Remove(var/message)
|
||||
if(isnum(message))
|
||||
for(var/list/stored_message in messages)
|
||||
if(stored_message["id"] == message)
|
||||
message = stored_message
|
||||
break
|
||||
messages -= list(message)
|
||||
/*
|
||||
Command action procs
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
// Opens the interface for the given air alarm.
|
||||
if("alarm")
|
||||
var/obj/structure/machinery/alarm/alarm = locate(params["alarm"]) in (monitored_alarms.len ? monitored_alarms : SSmachinery.processing)
|
||||
if(alarm)
|
||||
if(alarm?.rcon_setting)
|
||||
alarm.ui_interact(usr)
|
||||
return TRUE
|
||||
// Manually clear and repopulate the alarm list.
|
||||
@@ -65,6 +65,8 @@
|
||||
|
||||
var/alarms = list()
|
||||
for(var/obj/structure/machinery/alarm/alarm in monitored_alarms)
|
||||
if(!alarm.rcon_setting)
|
||||
continue
|
||||
alarms += list(list(
|
||||
"deck" = alarm.alarm_area.horizon_deck,
|
||||
"dept" = alarm.alarm_area.department,
|
||||
@@ -77,4 +79,3 @@
|
||||
data["alarms"] = alarms
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
..()
|
||||
lstate = SSnightlight.is_active() ? "dark" : "full"
|
||||
|
||||
/datum/computer_file/program/lighting_control/proc/update_lighting()
|
||||
/datum/computer_file/program/lighting_control/proc/update_lighting(var/new_context = context, var/new_lstate = lstate)
|
||||
context = new_context
|
||||
lstate = new_lstate
|
||||
|
||||
// whether to only select areas explicitly marked for nightlighting
|
||||
var/wl_only = context == "all" ? 0 : 1
|
||||
@@ -48,7 +50,8 @@
|
||||
lstate = params["mode"]
|
||||
. = TRUE
|
||||
if ("set")
|
||||
update_lighting()
|
||||
update_lighting(params["context"] || context, params["mode"] || lstate)
|
||||
. = TRUE
|
||||
|
||||
/datum/computer_file/program/lighting_control/ui_data(mob/user)
|
||||
var/list/data = initial_data()
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
color = LIGHT_COLOR_YELLOW
|
||||
tgui_id = "RCON"
|
||||
tgui_theme = "hephaestus"
|
||||
ui_auto_update = FALSE
|
||||
|
||||
/datum/computer_file/program/rcon_console/New()
|
||||
..()
|
||||
|
||||
@@ -162,9 +162,10 @@
|
||||
return FALSE
|
||||
|
||||
set_current(C)
|
||||
user.set_machine(ui_host())
|
||||
var/obj/host = ui_host()
|
||||
if(host)
|
||||
user.set_machine(host)
|
||||
user.reset_view(current_camera)
|
||||
check_eye(user)
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -172,6 +173,22 @@
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/camera_monitor/check_eye(mob/user)
|
||||
if(user.stat || user.blinded)
|
||||
return -1
|
||||
if(!current_camera)
|
||||
return 0
|
||||
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if(viewflag < 0)
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
/datum/computer_file/program/camera_monitor/grants_equipment_vision(mob/user)
|
||||
if(user.stat || user.blinded || !current_camera)
|
||||
return FALSE
|
||||
return current_camera.grants_equipment_vision(user)
|
||||
|
||||
/datum/computer_file/program/camera_monitor/proc/set_current(var/obj/structure/machinery/camera/C)
|
||||
if(current_camera == C)
|
||||
return
|
||||
@@ -192,44 +209,14 @@
|
||||
L.tracking_cancelled()
|
||||
current_camera = null
|
||||
|
||||
/datum/computer_file/program/camera_monitor/check_eye(var/mob/user as mob)
|
||||
var/obj/item/modular_computer/MC = user.machine
|
||||
if(istype(MC) && ui_host() == MC)
|
||||
if(!MC.working || user.blinded || user.stat)
|
||||
user.unset_machine()
|
||||
return -1
|
||||
if(!current_camera)
|
||||
return 0
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if ( viewflag < 0 ) //camera doesn't work
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
/datum/computer_file/program/camera_monitor/grants_equipment_vision(mob/user)
|
||||
var/obj/item/modular_computer/MC = user.machine
|
||||
if(istype(MC) && ui_host() == MC)
|
||||
if(!MC.working || user.blinded || user.stat)
|
||||
return FALSE
|
||||
if(!current_camera)
|
||||
return FALSE
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if (viewflag < 0) //camera doesn't work
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
// ERT Variant of the program
|
||||
/datum/computer_file/program/camera_monitor/ert
|
||||
filename = "ntcammon"
|
||||
filedesc = "Advanced Camera Monitoring"
|
||||
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements. This version has an integrated database with additional encrypted keys."
|
||||
size = 14
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/ert
|
||||
available_on_ntnet = FALSE
|
||||
|
||||
/datum/nano_module/camera_monitor/ert
|
||||
name = "Advanced Camera Monitoring Program"
|
||||
available_to_ai = FALSE
|
||||
|
||||
// The ERT variant has access to ERT and crescent cams, but still checks for accesses. ERT members should be able to use it.
|
||||
/datum/computer_file/program/camera_monitor/ert/modify_networks_list(var/list/networks)
|
||||
..()
|
||||
@@ -242,13 +229,9 @@
|
||||
filedesc = "Journalism Camera Monitoring"
|
||||
extended_desc = "This program allows remote access to station's camera system. Some camera networks may have additional access requirements. This version has has a connection to news networks."
|
||||
size = 2
|
||||
nanomodule_path = /datum/nano_module/camera_monitor/news
|
||||
required_access_download = null
|
||||
usage_flags = PROGRAM_ALL
|
||||
|
||||
/datum/nano_module/camera_monitor/news
|
||||
name = "Journalism Camera Monitoring Program"
|
||||
|
||||
/datum/computer_file/program/camera_monitor/news/modify_networks_list(var/list/networks)
|
||||
networks = list()
|
||||
networks.Add(list(list("tag" = NETWORK_NEWS, "has_access" = 1)))
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
set_current(C)
|
||||
user.machine = ui_host()
|
||||
user.reset_view(current_camera)
|
||||
check_eye(user)
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/penal_mechs/proc/set_current(var/obj/structure/machinery/camera/C)
|
||||
@@ -150,11 +149,3 @@
|
||||
if(istype(L))
|
||||
L.tracking_cancelled()
|
||||
current_camera = null
|
||||
|
||||
/datum/computer_file/program/penal_mechs/check_eye(var/mob/user)
|
||||
if(!current_camera)
|
||||
return FALSE
|
||||
var/viewflag = current_camera.check_eye(user)
|
||||
if(viewflag < 0) //camera doesn't work
|
||||
reset_current()
|
||||
return viewflag
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
undeletable = TRUE
|
||||
tgui_id = "FileManager"
|
||||
var/open_file
|
||||
var/open_file_is_usb = FALSE
|
||||
var/error
|
||||
|
||||
/datum/computer_file/program/filemanager/ui_data(mob/user)
|
||||
@@ -27,7 +28,11 @@
|
||||
if(!computer || !computer.hard_drive)
|
||||
data["error"] = "I/O ERROR: Unable to access hard drive."
|
||||
else
|
||||
HDD = computer.hard_drive
|
||||
HDD = open_file_is_usb ? computer.portable_drive : computer.hard_drive
|
||||
data["file_is_usb"] = open_file_is_usb
|
||||
if(!HDD)
|
||||
data["error"] = "I/O ERROR: Unable to open file."
|
||||
return data
|
||||
file = HDD.find_file_by_name(open_file)
|
||||
script = file
|
||||
if(!istype(file))
|
||||
@@ -45,6 +50,7 @@
|
||||
else
|
||||
data["scriptdata"] = null
|
||||
data["filedata"] = null
|
||||
data["file_is_usb"] = FALSE
|
||||
data["filename"] = null
|
||||
HDD = computer.hard_drive
|
||||
RHDD = computer.portable_drive
|
||||
@@ -85,10 +91,23 @@
|
||||
return
|
||||
if(F.can_access_file(usr))
|
||||
open_file = params["PRG_openfile"]
|
||||
open_file_is_usb = FALSE
|
||||
|
||||
if("PRG_usbopenfile")
|
||||
. = TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/RHDD = computer.portable_drive
|
||||
if(!RHDD)
|
||||
return
|
||||
var/datum/computer_file/F = RHDD.find_file_by_name(params["PRG_usbopenfile"])
|
||||
if(!F)
|
||||
return
|
||||
if(F.can_access_file(usr))
|
||||
open_file = params["PRG_usbopenfile"]
|
||||
open_file_is_usb = TRUE
|
||||
|
||||
if("PRG_newtextfile")
|
||||
. = TRUE
|
||||
var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename"))
|
||||
var/newname = sanitize_filename(tgui_input_text(usr, "Enter file name or leave blank to cancel:", "File rename"))
|
||||
if(!newname)
|
||||
return TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
@@ -97,7 +116,9 @@
|
||||
var/datum/computer_file/data/F = new/datum/computer_file/data()
|
||||
F.filename = newname
|
||||
F.filetype = "TXT"
|
||||
HDD.store_file(F)
|
||||
if(!HDD.store_file(F))
|
||||
qdel(F)
|
||||
error = "I/O ERROR: Unable to create file. The hard drive may be full, read-only, or contain a conflicting file."
|
||||
|
||||
if("PRG_deletefile")
|
||||
. = TRUE
|
||||
@@ -122,6 +143,7 @@
|
||||
if("PRG_closefile")
|
||||
. = TRUE
|
||||
open_file = null
|
||||
open_file_is_usb = FALSE
|
||||
error = null
|
||||
|
||||
if("PRG_clone")
|
||||
@@ -132,8 +154,13 @@
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(params["PRG_clone"])
|
||||
if(!F || !istype(F))
|
||||
return TRUE
|
||||
if(istype(F, /datum/computer_file/program))
|
||||
error = "I/O ERROR: Executable programs cannot be cloned from File Manager."
|
||||
return TRUE
|
||||
var/datum/computer_file/C = F.clone(1)
|
||||
HDD.store_file(C)
|
||||
if(!HDD.store_file(C))
|
||||
qdel(C)
|
||||
error = "I/O ERROR: Unable to clone file. The hard drive may be full, read-only, or contain a conflicting file."
|
||||
|
||||
if("PRG_rename")
|
||||
. = TRUE
|
||||
@@ -143,7 +170,7 @@
|
||||
var/datum/computer_file/file = HDD.find_file_by_name(params["PRG_rename"])
|
||||
if(!file || !istype(file))
|
||||
return TRUE
|
||||
var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename))
|
||||
var/newname = sanitize_filename(tgui_input_text(usr, "Enter new file name or leave blank to cancel:", "File rename", file.filename))
|
||||
if(file && newname)
|
||||
file.filename = newname
|
||||
|
||||
@@ -163,7 +190,7 @@
|
||||
var/oldtext = html_decode(F.stored_data)
|
||||
oldtext = replacetext(oldtext, "\[editorbr\]", "\n")
|
||||
|
||||
var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[editorbr\]"), MAX_TEXTFILE_LENGTH)
|
||||
var/newtext = sanitize_filename(replacetext(tgui_input_text(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext), "\n", "\[editorbr\]"), MAX_TEXTFILE_LENGTH)
|
||||
if(!newtext)
|
||||
return
|
||||
|
||||
@@ -185,10 +212,10 @@
|
||||
return TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
if(!HDD)
|
||||
return TRUE
|
||||
return FALSE
|
||||
if(!computer.nano_printer)
|
||||
error = "Missing Hardware: Your computer does not have required hardware to complete this operation."
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/datum/computer_file/data/F = HDD.find_file_by_name(open_file)
|
||||
var/datum/computer_file/script/S = F
|
||||
if(!F)
|
||||
@@ -196,21 +223,21 @@
|
||||
if(istype(F))
|
||||
if(!computer.nano_printer.print_text(F.stored_data))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return TRUE
|
||||
return FALSE
|
||||
else if(istype(S))
|
||||
if(!computer.nano_printer.print_text(S.code))
|
||||
error = "Hardware error: Printer was unable to print the file. It may be out of paper."
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
if("PRG_copytousb")
|
||||
. = TRUE
|
||||
var/obj/item/computer_hardware/hard_drive/HDD = computer.hard_drive
|
||||
var/obj/item/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive
|
||||
if(!HDD || !RHDD)
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/datum/computer_file/F = HDD.find_file_by_name(params["PRG_copytousb"])
|
||||
if(!F || !istype(F))
|
||||
return TRUE
|
||||
return FALSE
|
||||
var/is_usr_tech_support = FALSE
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
@@ -270,7 +297,7 @@
|
||||
return
|
||||
if(F.password)
|
||||
return
|
||||
F.password = sanitize(input(usr, "Enter an encryption key:", "Encrypt File"))
|
||||
F.password = sanitize_filename(tgui_input_text(usr, "Enter an encryption key:", "Encrypt File"))
|
||||
|
||||
if("PRG_decrypt")
|
||||
. = TRUE
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/datum/nano_module
|
||||
var/name
|
||||
var/datum/host
|
||||
var/available_to_ai = TRUE
|
||||
var/datum/topic_manager/topic_manager
|
||||
|
||||
/datum/nano_module/New(var/datum/host, var/topic_manager)
|
||||
..()
|
||||
src.host = host
|
||||
src.topic_manager = topic_manager
|
||||
|
||||
/datum/nano_module/Destroy()
|
||||
host = null
|
||||
topic_manager = null
|
||||
return ..()
|
||||
|
||||
/datum/nano_module/ui_host()
|
||||
return host ? host.ui_host() : src
|
||||
|
||||
/datum/nano_module/proc/can_still_topic(var/datum/ui_state/state = GLOB.default_state)
|
||||
return CanUseTopic(usr, state) == STATUS_INTERACTIVE
|
||||
|
||||
/datum/nano_module/proc/check_eye(var/mob/user)
|
||||
return -1
|
||||
|
||||
/datum/nano_module/proc/grants_equipment_vision(var/mob/user)
|
||||
return FALSE
|
||||
|
||||
/datum/nano_module/proc/check_access(var/mob/user, var/access)
|
||||
if(!access)
|
||||
return 1
|
||||
|
||||
if(!istype(user))
|
||||
return 0
|
||||
|
||||
var/obj/item/card/id/I = user.GetIdCard()
|
||||
if(!I)
|
||||
return 0
|
||||
|
||||
if(access in I.access)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
/datum/nano_module/Topic(href, href_list)
|
||||
if(topic_manager && topic_manager.Topic(href, href_list))
|
||||
return TRUE
|
||||
. = ..()
|
||||
|
||||
/datum/nano_module/nano_host()
|
||||
return host ? host : src
|
||||
|
||||
/datum/nano_module/proc/get_host_z()
|
||||
return GET_Z(nano_host())
|
||||
|
||||
/datum/proc/initial_data()
|
||||
return list()
|
||||
|
||||
/datum/proc/update_layout()
|
||||
return FALSE
|
||||
|
||||
/datum/proc/nano_host()
|
||||
return src
|
||||
@@ -1,37 +0,0 @@
|
||||
// This file contains all Nano procs/definitions for external classes/objects
|
||||
|
||||
/**
|
||||
* Called when a Nano UI window is closed
|
||||
* This is how Nano handles closed windows
|
||||
* It must be a verb so that it can be called using winset
|
||||
*/
|
||||
/client/verb/nanoclose(var/uiref as text)
|
||||
set hidden = 1 // hide this verb from the user's panel
|
||||
set name = "nanoclose"
|
||||
|
||||
var/datum/nanoui/ui = locate(uiref)
|
||||
|
||||
if (istype(ui))
|
||||
ui.close()
|
||||
|
||||
if(ui.ref)
|
||||
var/href = "close=1"
|
||||
src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic()
|
||||
else if (ui.on_close_logic)
|
||||
// no atomref specified (or not found)
|
||||
// so just reset the user mob's machine var
|
||||
if(src && src.mob)
|
||||
src.mob.unset_machine()
|
||||
|
||||
/**
|
||||
* The ui_interact proc is used to open and update Nano UIs
|
||||
* If ui_interact is not used then the UI will not update correctly
|
||||
* ui_interact is currently defined for /atom/movable
|
||||
*
|
||||
* @param user /mob The mob who is interacting with this ui
|
||||
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
|
||||
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
|
||||
* @param force_open boolean Force the UI to (re)open, even if it's already open
|
||||
*/
|
||||
/datum/proc/nanoui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nanoui/master_ui = null, var/datum/ui_state/state = GLOB.default_state)
|
||||
return
|
||||
@@ -1,90 +0,0 @@
|
||||
// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm
|
||||
|
||||
#define NANOMAP_ICON_SIZE 4
|
||||
#define NANOMAP_MAX_ICON_DIMENSION 1024
|
||||
|
||||
#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE)
|
||||
|
||||
#define NANOMAP_TERMINALERR 5
|
||||
#define NANOMAP_INPROGRESS 2
|
||||
#define NANOMAP_BADOUTPUT 2
|
||||
#define NANOMAP_SUCCESS 1
|
||||
#define NANOMAP_WATCHDOGSUCCESS 4
|
||||
#define NANOMAP_WATCHDOGTERMINATE 3
|
||||
|
||||
|
||||
//Call these procs to dump your world to a series of image files (!!)
|
||||
//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly
|
||||
|
||||
/client/proc/nanomapgen_DumpImage()
|
||||
set name = "Generate NanoUI Map"
|
||||
set category = "Server"
|
||||
|
||||
if(holder)
|
||||
nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate")))
|
||||
|
||||
/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1)
|
||||
|
||||
if (endX < 0 || endX > world.maxx)
|
||||
endX = world.maxx
|
||||
|
||||
if (endY < 0 || endY > world.maxy)
|
||||
endY = world.maxy
|
||||
|
||||
if (currentZ < 0 || currentZ > world.maxz)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]</B>")
|
||||
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
if (startX > endX)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: startX ([startX]) cannot be greater than endX ([endX])</B>")
|
||||
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
if (startY > endX)
|
||||
to_chat(usr, "NanoMapGen: <B>ERROR: startY ([startY]) cannot be greater than endY ([endY])</B>")
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
var/icon/Tile = icon(file("nano/mapbase1024.png"))
|
||||
if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
|
||||
log_world("ERROR: NanoMapGen: <B>ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]</B>")
|
||||
sleep(3)
|
||||
return NANOMAP_TERMINALERR
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>")
|
||||
to_chat(usr, "NanoMapGen: <B>GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])</B>")
|
||||
|
||||
var/count = 0;
|
||||
for(var/WorldX = startX, WorldX <= endX, WorldX++)
|
||||
for(var/WorldY = startY, WorldY <= endY, WorldY++)
|
||||
|
||||
var/atom/Turf = locate(WorldX, WorldY, currentZ)
|
||||
|
||||
var/icon/TurfIcon = new(Turf.icon, Turf.icon_state, dir = Turf.dir)
|
||||
TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE)
|
||||
|
||||
Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE))
|
||||
|
||||
count++
|
||||
|
||||
if (count % 8000 == 0)
|
||||
LOG_DEBUG("NanoMapGen: <B>[count] tiles done</B>")
|
||||
sleep(1)
|
||||
|
||||
var/mapFilename = "new_Aurora[currentZ]"
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>sending [mapFilename] to client</B>")
|
||||
|
||||
usr << browse(Tile, "window=picture;file=[mapFilename];display=0")
|
||||
|
||||
LOG_DEBUG("NanoMapGen: <B>Done.</B>")
|
||||
|
||||
to_chat(usr, "NanoMapGen: <B>Done. File [mapFilename] uploaded to your cache.</B>")
|
||||
|
||||
if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION)
|
||||
return NANOMAP_BADOUTPUT
|
||||
|
||||
return NANOMAP_SUCCESS
|
||||
@@ -1,537 +0,0 @@
|
||||
/**********************************************************
|
||||
NANO UI FRAMEWORK
|
||||
|
||||
nanoui class (or whatever Byond calls classes)
|
||||
|
||||
nanoui is used to open and update nano browser uis
|
||||
**********************************************************/
|
||||
|
||||
/datum/nanoui
|
||||
// the user who opened this ui
|
||||
var/mob/user
|
||||
// the object this ui "belongs" to
|
||||
var/datum/src_object
|
||||
// the title of this ui
|
||||
var/title
|
||||
// the key of this ui, this is to allow multiple (different) uis for each src_object
|
||||
var/ui_key
|
||||
// window_id is used as the window name/identifier for browse and onclose
|
||||
var/window_id
|
||||
// the browser window width
|
||||
var/width = 0
|
||||
// the browser window height
|
||||
var/height = 0
|
||||
// whether to use extra logic when window closes
|
||||
var/on_close_logic = 1
|
||||
// an extra ref to use when the window is closed, usually null
|
||||
var/atom/ref = null
|
||||
// options for modifying window behaviour
|
||||
var/window_options = "focus=0;can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
|
||||
// the list of stylesheets to apply to this ui
|
||||
var/list/stylesheets = list()
|
||||
// the list of javascript scripts to use for this ui
|
||||
var/list/scripts = list()
|
||||
// a list of templates which can be used with this ui
|
||||
var/templates[0]
|
||||
// the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
|
||||
var/layout_key = "default"
|
||||
// this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use)
|
||||
var/auto_update_layout = 0
|
||||
// this sets whether to re-render the ui content with each update (default 1)
|
||||
var/auto_update_content = 1
|
||||
// the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing)
|
||||
var/state_key = "default"
|
||||
// show the map ui, this is used by the default layout
|
||||
var/show_map = 0
|
||||
// the map z level to display
|
||||
var/map_z_level = 3
|
||||
// initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on)
|
||||
var/list/initial_data[0]
|
||||
// set to 1 to update the ui automatically every master_controller tick
|
||||
var/is_auto_updating = 0
|
||||
// the current status/visibility of the ui
|
||||
var/status = STATUS_INTERACTIVE
|
||||
|
||||
// Relationship between a master interface and its children. Used in update_status
|
||||
var/datum/nanoui/master_ui
|
||||
var/list/datum/nanoui/children = list()
|
||||
var/datum/ui_state/state = null
|
||||
|
||||
/**
|
||||
* Create a new nanoui instance.
|
||||
*
|
||||
* @param nuser /mob The mob who has opened/owns this ui
|
||||
* @param nsrc_object /obj|/mob The obj or mob which this ui belongs to
|
||||
* @param nui_key string A string key to use for this ui. Allows for multiple unique uis on one src_oject
|
||||
* @param ntemplate string The filename of the template file from /nano/templates (e.g. "my_template.tmpl")
|
||||
* @param ntitle string The title of this ui
|
||||
* @param nwidth int the width of the ui window
|
||||
* @param nheight int the height of the ui window
|
||||
* @param nref /atom A custom ref to use if "on_close_logic" is set to 1
|
||||
*
|
||||
* @return /nanoui new nanoui object
|
||||
*/
|
||||
/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/ui_state/state = GLOB.default_state)
|
||||
user = nuser
|
||||
src_object = nsrc_object
|
||||
ui_key = nui_key
|
||||
window_id = "[ui_key][REF(src_object)]"
|
||||
|
||||
src.master_ui = master_ui
|
||||
if(master_ui)
|
||||
master_ui.children += src
|
||||
src.state = state
|
||||
|
||||
// add the passed template filename as the "main" template, this is required
|
||||
add_template("main", ntemplate_filename)
|
||||
|
||||
if (ntitle)
|
||||
title = sanitize(ntitle)
|
||||
if (nwidth)
|
||||
width = nwidth
|
||||
if (nheight)
|
||||
height = nheight
|
||||
if (nref)
|
||||
ref = nref
|
||||
|
||||
add_common_assets()
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui)
|
||||
assets.send(user, assets)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Use this proc to add assets which are common to (and required by) all nano uis
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_common_assets()
|
||||
add_script("https://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js")
|
||||
add_script("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js")
|
||||
add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together)
|
||||
add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions.
|
||||
add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates.
|
||||
add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state
|
||||
add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from
|
||||
add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState
|
||||
add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs
|
||||
add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs
|
||||
add_stylesheet("shared.css") // this CSS sheet is common to all UIs
|
||||
add_stylesheet("icons.css") // this CSS sheet is common to all UIs
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param push_update int (bool) Push an update to the ui to update it's status (an update is always sent if the status has changed to red (0))
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_status(state, push_update)
|
||||
if (state != status) // Only update if it is different
|
||||
if (status == STATUS_DISABLED)
|
||||
status = state
|
||||
if (push_update)
|
||||
update()
|
||||
else
|
||||
status = state
|
||||
if (push_update || status == 0)
|
||||
push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used
|
||||
|
||||
/**
|
||||
* Update the status (visibility) of this ui based on the user's status
|
||||
*
|
||||
* @param push_update int (bool) Push an update to the ui to update it's status. This is set to 0/false if an update is going to be pushed anyway (to avoid unnessary updates)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/update_status(var/push_update = 0)
|
||||
var/obj/host = src_object.ui_host()
|
||||
var/new_status = host.CanUseTopic(user, state)
|
||||
if(master_ui)
|
||||
new_status = min(new_status, master_ui.status)
|
||||
|
||||
set_status(new_status, push_update)
|
||||
if(new_status == STATUS_CLOSE)
|
||||
close()
|
||||
|
||||
/**
|
||||
* Set the ui to auto update (every master_controller tick)
|
||||
*
|
||||
* @param state int (bool) Set auto update to 1 or 0 (true/false)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update(nstate = 1)
|
||||
is_auto_updating = nstate
|
||||
|
||||
/**
|
||||
* Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates.
|
||||
*
|
||||
* @param data /list The list of data for this ui
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_initial_data(list/data)
|
||||
initial_data = data
|
||||
|
||||
/**
|
||||
* Get config data to sent to the ui.
|
||||
*
|
||||
* @return /list config data
|
||||
*/
|
||||
/datum/nanoui/proc/get_config_data()
|
||||
var/name = "[src_object]"
|
||||
name = sanitize(name)
|
||||
var/list/config_data = list(
|
||||
"title" = title,
|
||||
"srcObject" = list("name" = name),
|
||||
"stateKey" = state_key,
|
||||
"status" = status,
|
||||
"autoUpdateLayout" = auto_update_layout,
|
||||
"autoUpdateContent" = auto_update_content,
|
||||
"showMap" = show_map,
|
||||
"mapName" = "Aurora",
|
||||
"mapZLevel" = map_z_level,
|
||||
"mapZLevels" = SSatlas.current_map.map_levels,
|
||||
"user" = list("name" = user.name)
|
||||
)
|
||||
return config_data
|
||||
|
||||
/**
|
||||
* Get data to sent to the ui.
|
||||
*
|
||||
* @param data /list The list of general data for this ui (can be null to use previous data sent)
|
||||
*
|
||||
* @return /list data to send to the ui
|
||||
*/
|
||||
/datum/nanoui/proc/get_send_data(var/list/data)
|
||||
var/list/config_data = get_config_data()
|
||||
|
||||
var/list/send_data = list("config" = config_data)
|
||||
|
||||
if (!isnull(data))
|
||||
send_data["data"] = data
|
||||
|
||||
return send_data
|
||||
|
||||
/**
|
||||
* Set the browser window options for this ui
|
||||
*
|
||||
* @param nwindow_options string The new window options
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_window_options(nwindow_options)
|
||||
window_options = nwindow_options
|
||||
|
||||
/**
|
||||
* Add a CSS stylesheet to this UI
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param file string The name of the CSS file from /nano/css (e.g. "my_style.css")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_stylesheet(file)
|
||||
stylesheets.Add(file)
|
||||
|
||||
/**
|
||||
* Add a JavsScript script to this UI
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param file string The name of the JavaScript file from /nano/js (e.g. "my_script.js")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_script(file)
|
||||
scripts.Add(file)
|
||||
|
||||
/**
|
||||
* Add a template for this UI
|
||||
* Templates are combined with the data sent to the UI to create the rendered view
|
||||
* These must be added before the UI has been opened, adding after that will have no effect
|
||||
*
|
||||
* @param key string The key which is used to reference this template in the frontend
|
||||
* @param filename string The name of the template file from /nano/templates (e.g. "my_template.tmpl")
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/add_template(key, filename)
|
||||
templates[key] = filename
|
||||
|
||||
/**
|
||||
* Set the layout key for use in the frontend Javascript
|
||||
* The layout key is the basic layout key for the page
|
||||
* Two files are loaded on the client based on the layout key varable:
|
||||
* -> a template in /nano/templates with the filename "layout_<layout_key>.tmpl
|
||||
* -> a CSS stylesheet in /nano/css with the filename "layout_<layout_key>.css
|
||||
*
|
||||
* @param nlayout string The layout key to use
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_layout_key(nlayout_key)
|
||||
layout_key = lowertext(nlayout_key)
|
||||
|
||||
/**
|
||||
* Set the ui to update the layout (re-render it) on each update, turning this on will break the map ui (if it's being used)
|
||||
*
|
||||
* @param state int (bool) Set update to 1 or 0 (true/false) (default 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update_layout(nstate)
|
||||
auto_update_layout = nstate
|
||||
|
||||
/**
|
||||
* Set the ui to update the main content (re-render it) on each update
|
||||
*
|
||||
* @param state int (bool) Set update to 1 or 0 (true/false) (default 1)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_auto_update_content(nstate)
|
||||
auto_update_content = nstate
|
||||
|
||||
/**
|
||||
* Set the state key for use in the frontend Javascript
|
||||
*
|
||||
* @param nstate_key string The key of the state to use
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_state_key(nstate_key)
|
||||
state_key = nstate_key
|
||||
|
||||
/**
|
||||
* Toggle showing the map ui
|
||||
*
|
||||
* @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_show_map(nstate)
|
||||
show_map = nstate
|
||||
|
||||
/**
|
||||
* Toggle showing the map ui
|
||||
*
|
||||
* @param nstate_key boolean 1 to show map, 0 to hide (default is 0)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/set_map_z_level(nz)
|
||||
map_z_level = nz
|
||||
|
||||
/**
|
||||
* Set whether or not to use the "old" on close logic (mainly unset_machine())
|
||||
*
|
||||
* @param state int (bool) Set on_close_logic to 1 or 0 (true/false)
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/use_on_close_logic(state)
|
||||
on_close_logic = state
|
||||
|
||||
/**
|
||||
* Return the HTML for this UI
|
||||
*
|
||||
* @return string HTML for the UI
|
||||
*/
|
||||
/datum/nanoui/proc/get_html()
|
||||
|
||||
// before the UI opens, add the layout files based on the layout key
|
||||
add_stylesheet("layout_[layout_key].css")
|
||||
add_template("layout", "layout_[layout_key].tmpl")
|
||||
|
||||
var/head_content = ""
|
||||
|
||||
for (var/filename in scripts)
|
||||
head_content += "<script type='text/javascript' src='[filename]'></script> "
|
||||
|
||||
for (var/filename in stylesheets)
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[filename]'> "
|
||||
|
||||
var/template_data_json = "{}" // An empty JSON object
|
||||
if (templates.len > 0)
|
||||
template_data_json = strip_improper(json_encode(templates))
|
||||
|
||||
var/list/send_data = get_send_data(initial_data)
|
||||
var/initial_data_json = replacetext(replacetext(json_encode(send_data), """, "&#34;"), "'", "'")
|
||||
initial_data_json = strip_improper(initial_data_json);
|
||||
|
||||
var/url_parameters_json = json_encode(list("src" = "[REF(src)]"))
|
||||
|
||||
return {"
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<head>
|
||||
<script type='text/javascript'>
|
||||
function receiveUpdateData(jsonString)
|
||||
{
|
||||
// We need both jQuery and NanoStateManager to be able to receive data
|
||||
// At the moment any data received before those libraries are loaded will be lost
|
||||
if (typeof NanoStateManager != 'undefined' && typeof jQuery != 'undefined')
|
||||
{
|
||||
NanoStateManager.receiveUpdateData(jsonString);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
[head_content]
|
||||
</head>
|
||||
<body scroll=auto data-template-data='[template_data_json]' data-url-parameters='[url_parameters_json]' data-initial-data='[initial_data_json]'>
|
||||
<div id='uiLayout'>
|
||||
</div>
|
||||
<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>
|
||||
</html>
|
||||
"}
|
||||
|
||||
/**
|
||||
* Open this UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/open()
|
||||
if(!user.client)
|
||||
return
|
||||
|
||||
// An attempted fix to UIs sometimes locking up spamming runtime errors due to src_object being null for whatever reason.
|
||||
// This hard-deletes the UI, preventing the device that uses the UI from being locked up permanently.
|
||||
if(!src_object)
|
||||
del(src)
|
||||
|
||||
var/window_size = ""
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
update_status(0)
|
||||
if(status == STATUS_CLOSE)
|
||||
return
|
||||
|
||||
user << browse(get_html(), "window=[window_id];[window_size][window_options]")
|
||||
winset(user, "mapwindow.map", "focus=true") // return keyboard focus to map
|
||||
on_close_winset()
|
||||
//onclose(user, window_id)
|
||||
SSnanoui.ui_opened(src)
|
||||
|
||||
/**
|
||||
* Reinitialise this UI, potentially with a different template and/or initial data
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/reinitialise(template, new_initial_data)
|
||||
if(template)
|
||||
add_template("main", template)
|
||||
if(new_initial_data)
|
||||
set_initial_data(new_initial_data)
|
||||
open()
|
||||
|
||||
/**
|
||||
* Close this UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/close()
|
||||
is_auto_updating = 0
|
||||
SSnanoui.ui_closed(src)
|
||||
user << browse(null, "window=[window_id]")
|
||||
for(var/datum/nanoui/child in children)
|
||||
child.close()
|
||||
children.Cut()
|
||||
state = null
|
||||
master_ui = null
|
||||
|
||||
if(istype(user))
|
||||
user.unset_machine()
|
||||
|
||||
/**
|
||||
* Set the UI window to call the nanoclose verb when the window is closed
|
||||
* This allows Nano to handle closed windows
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/on_close_winset()
|
||||
if(!user.client)
|
||||
return
|
||||
var/params = "[REF(src)]"
|
||||
|
||||
winset(user, window_id, "on-close=\"nanoclose [params]\"")
|
||||
|
||||
/**
|
||||
* Push data to an already open UI window
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/push_data(data, force_push = 0)
|
||||
update_status(0)
|
||||
if (status == STATUS_DISABLED && !force_push)
|
||||
return // Cannot update UI, no visibility
|
||||
|
||||
var/list/send_data = get_send_data(data)
|
||||
|
||||
//to_chat(user, list2json(data) // used for debugging)
|
||||
send_output(user, list2params(list(json_encode(send_data))),"[window_id].browser:receiveUpdateData")
|
||||
|
||||
/**
|
||||
* This Topic() proc is called whenever a user clicks on a link within a Nano UI
|
||||
* If the UI status is currently STATUS_INTERACTIVE then call the src_object Topic()
|
||||
* If the src_object Topic() returns 1 (true) then update all UIs attached to src_object
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/Topic(href, href_list)
|
||||
update_status(0) // update the status
|
||||
if (status != STATUS_INTERACTIVE || user != usr) // If UI is not interactive or usr calling Topic is not the UI user
|
||||
return
|
||||
|
||||
// This is used to toggle the nano map ui
|
||||
var/map_update = 0
|
||||
if(href_list["showMap"])
|
||||
set_show_map(text2num(href_list["showMap"]))
|
||||
map_update = 1
|
||||
|
||||
if(href_list["mapZLevel"])
|
||||
var/map_z = (text2num(href_list["mapZLevel"]))
|
||||
if(map_z in SSatlas.current_map.map_levels)
|
||||
set_map_z_level(map_z)
|
||||
map_update = 1
|
||||
else
|
||||
return
|
||||
|
||||
if ((src_object && src_object.Topic(href, href_list, state)) || map_update)
|
||||
SSnanoui.update_uis(src_object) // update all UIs attached to src_object
|
||||
|
||||
/**
|
||||
* Process this UI, updating the entire UI or just the status (aka visibility)
|
||||
* This process proc is called by the master_controller
|
||||
*
|
||||
* @param update string For this UI to update
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/process(update = 0)
|
||||
if (QDELETED(src_object) || QDELETED(user))
|
||||
close()
|
||||
return
|
||||
|
||||
if (status && (update || is_auto_updating))
|
||||
update() // Update the UI (update_status() is called whenever a UI is updated)
|
||||
else
|
||||
update_status(1) // Not updating UI, so lets check here if status has changed
|
||||
|
||||
/**
|
||||
* Update the UI
|
||||
*
|
||||
* @return nothing
|
||||
*/
|
||||
/datum/nanoui/proc/update(var/force_open = 0)
|
||||
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
|
||||
@@ -399,17 +399,34 @@
|
||||
|
||||
ui_interact(user)
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Tank", ui_x=400, ui_y=180)
|
||||
ui.open()
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_host(mob/user)
|
||||
. = ..()
|
||||
if(istype(loc,/obj/item/transfer_valve))
|
||||
return loc
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_data(mob/user)
|
||||
var/mob/living/carbon/location = null
|
||||
|
||||
if(istype(loc, /obj/item/rig))
|
||||
if(istype(loc.loc, /mob/living/carbon))
|
||||
location = loc.loc
|
||||
else if(istype(loc, /mob/living/carbon))
|
||||
location = loc
|
||||
|
||||
var/using_internal
|
||||
if(istype(location))
|
||||
if(location.internal==src)
|
||||
using_internal = 1
|
||||
|
||||
// this is the data which will be sent to the ui
|
||||
var/data[0]
|
||||
data["tankPressure"] = round(SAFE_XGM_PRESSURE(air_contents))
|
||||
var/list/data = list()
|
||||
|
||||
data["tankPressure"] = round(XGM_PRESSURE(air_contents))
|
||||
data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0)
|
||||
data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE)
|
||||
data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE)
|
||||
@@ -437,36 +454,13 @@
|
||||
if(H.head && (H.head.item_flags & ITEM_FLAG_AIRTIGHT))
|
||||
data["maskConnected"] = 1
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
|
||||
if (!ui)
|
||||
// the ui does not exist, so we'll create a new() one
|
||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
||||
ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300)
|
||||
// when the ui is first opened this is the data it will use
|
||||
ui.set_initial_data(data)
|
||||
// open the new ui window
|
||||
ui.open()
|
||||
// auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
return data
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/Topic(href, href_list)
|
||||
..()
|
||||
if (usr.stat|| usr.restrained())
|
||||
return 0
|
||||
if (src.loc != usr)
|
||||
return 0
|
||||
|
||||
if (href_list["dist_p"])
|
||||
if (href_list["dist_p"] == "reset")
|
||||
src.distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE
|
||||
else if (href_list["dist_p"] == "max")
|
||||
src.distribute_pressure = TANK_MAX_RELEASE_PRESSURE
|
||||
else
|
||||
var/cp = text2num(href_list["dist_p"])
|
||||
src.distribute_pressure += cp
|
||||
src.distribute_pressure = min(max(round(src.distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE)
|
||||
if (href_list["stat"])
|
||||
/obj/item/organ/internal/vaurca/preserve/ui_act(action,params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(action=="toggleReleaseValve")
|
||||
if(istype(loc,/mob/living/carbon))
|
||||
var/mob/living/carbon/location = loc
|
||||
if(location.internal == src)
|
||||
@@ -476,7 +470,6 @@
|
||||
if (location.internals)
|
||||
location.internals.icon_state = "internal0"
|
||||
else
|
||||
|
||||
var/can_open_valve
|
||||
if(location.wear_mask && (location.wear_mask.item_flags & ITEM_FLAG_AIRTIGHT))
|
||||
can_open_valve = 1
|
||||
@@ -491,11 +484,12 @@
|
||||
if (location.internals)
|
||||
location.internals.icon_state = "internal1"
|
||||
else
|
||||
to_chat(usr, SPAN_NOTICE("You need something to connect to \the [src]."))
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
return 1
|
||||
|
||||
to_chat(usr, SPAN_WARNING("You need something to connect to \the [src]."))
|
||||
. = TRUE
|
||||
update_icon()
|
||||
if(action=="setReleasePressure")
|
||||
distribute_pressure = min(max(round(text2num(params["release_pressure"])), 0), TANK_MAX_RELEASE_PRESSURE)
|
||||
. = TRUE
|
||||
|
||||
/obj/item/organ/internal/vaurca/preserve/remove_air(amount)
|
||||
return air_contents.remove(amount)
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
/obj/structure/machinery/computer/ship/engines/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "ship control systems")
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
|
||||
@@ -117,6 +117,10 @@
|
||||
return 1
|
||||
|
||||
/obj/structure/machinery/computer/ship/helm/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Helm", capitalize_first_letters(name))
|
||||
@@ -126,55 +130,52 @@
|
||||
/obj/structure/machinery/computer/ship/helm/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "helm")
|
||||
else
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["landed"] = connected.get_landed_info()
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["dest"] = dy && dx
|
||||
data["autopilot_x"] = dx
|
||||
data["autopilot_y"] = dy
|
||||
data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted"
|
||||
data["accel"] = get_acceleration()
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["autopilot"] = autopilot
|
||||
data["manual_control"] = viewing_overmap(user)
|
||||
data["canburn"] = connected.can_burn()
|
||||
data["canturn"] = connected.can_turn()
|
||||
data["cancombatroll"] = connected.can_combat_roll()
|
||||
data["cancombatturn"] = connected.can_combat_turn()
|
||||
data["accellimit"] = accellimit*1000
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["landed"] = connected.get_landed_info()
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["dest"] = dy && dx
|
||||
data["autopilot_x"] = dx
|
||||
data["autopilot_y"] = dy
|
||||
data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted"
|
||||
data["accel"] = get_acceleration()
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["autopilot"] = autopilot
|
||||
data["manual_control"] = viewing_overmap(user)
|
||||
data["canburn"] = connected.can_burn()
|
||||
data["canturn"] = connected.can_turn()
|
||||
data["cancombatroll"] = connected.can_combat_roll()
|
||||
data["cancombatturn"] = connected.can_combat_turn()
|
||||
data["accellimit"] = accellimit*1000
|
||||
|
||||
var/speed = round(connected.get_speed()*1000, 0.01)
|
||||
data["speed"] = speed
|
||||
if(connected.get_speed() < SHIP_SPEED_SLOW)
|
||||
data["speed_slow"] = TRUE
|
||||
if(connected.get_speed() > SHIP_SPEED_FAST)
|
||||
data["speed_fast"] = TRUE
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
var/speed = round(connected.get_speed()*1000, 0.01)
|
||||
data["speed"] = speed
|
||||
if(connected.get_speed() < SHIP_SPEED_SLOW)
|
||||
data["speed_slow"] = TRUE
|
||||
if(connected.get_speed() > SHIP_SPEED_FAST)
|
||||
data["speed_fast"] = TRUE
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
|
||||
data["ETAnext"] = get_eta()
|
||||
data["ETAnext"] = get_eta()
|
||||
|
||||
var/list/locations[0]
|
||||
for (var/key in known_sectors)
|
||||
var/datum/computer_file/data/waypoint/R = known_sectors[key]
|
||||
var/list/rdata[0]
|
||||
rdata["name"] = R.fields["name"]
|
||||
rdata["x"] = R.fields["x"]
|
||||
rdata["y"] = R.fields["y"]
|
||||
rdata["reference"] = "[REF(R)]"
|
||||
locations.Add(list(rdata))
|
||||
var/list/locations[0]
|
||||
for (var/key in known_sectors)
|
||||
var/datum/computer_file/data/waypoint/R = known_sectors[key]
|
||||
var/list/rdata[0]
|
||||
rdata["name"] = R.fields["name"]
|
||||
rdata["x"] = R.fields["x"]
|
||||
rdata["y"] = R.fields["y"]
|
||||
rdata["reference"] = "[REF(R)]"
|
||||
locations.Add(list(rdata))
|
||||
|
||||
data["locations"] = locations
|
||||
data["locations"] = locations
|
||||
|
||||
return data
|
||||
|
||||
@@ -367,6 +368,10 @@
|
||||
ui_interact(user)
|
||||
|
||||
/obj/structure/machinery/computer/ship/navigation/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(!connected)
|
||||
balloon_alert(user, "no connection!")
|
||||
return
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Nav", capitalize_first_letters(name), ui_x=470, ui_y=320)
|
||||
@@ -376,25 +381,22 @@
|
||||
/obj/structure/machinery/computer/ship/navigation/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(!connected)
|
||||
display_reconnect_dialog(user, "navigation")
|
||||
else
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
var/turf/T = get_turf(connected)
|
||||
var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
|
||||
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["speed"] = round(connected.get_speed()*1000, 0.01)
|
||||
data["accel"] = round(connected.get_acceleration()*1000, 0.01)
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["ETAnext"] = get_eta()
|
||||
data["viewing"] = viewing_overmap(user)
|
||||
data["sector"] = current_sector ? current_sector.name : "Deep Space"
|
||||
data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
|
||||
data["ship_coord_x"] = connected.x
|
||||
data["ship_coord_y"] = connected.y
|
||||
data["speed"] = round(connected.get_speed()*1000, 0.01)
|
||||
data["accel"] = round(connected.get_acceleration()*1000, 0.01)
|
||||
var/list/speed_xy = connected.get_speed_xy()
|
||||
data["ship_speed_x"] = speed_xy[1]
|
||||
data["ship_speed_y"] = speed_xy[2]
|
||||
data["direction"] = dir2angle(connected.dir)
|
||||
data["heading"] = connected.get_heading() ? dir2angle(connected.get_heading()) : 0
|
||||
data["ETAnext"] = get_eta()
|
||||
data["viewing"] = viewing_overmap(user)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -22,11 +22,6 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
|
||||
. += "Consoles like these are typically access-locked."
|
||||
. += "You can remove this lock with <b>wirecutters</b>, but it would take awhile! Alternatively, you can also use a cryptographic sequencer (emag) for instant removal."
|
||||
|
||||
/obj/structure/machinery/computer/ship/proc/display_reconnect_dialog(var/mob/user, var/flavor)
|
||||
var/datum/browser/popup = new (user, "[src]", "[src]")
|
||||
popup.set_content("<center><strong><font color = 'red'>Error</strong></font><br>Unable to connect to [flavor].<br><a href='byond://?src=[REF(src)];sync=1'>Reconnect</a></center>")
|
||||
popup.open()
|
||||
|
||||
/obj/structure/machinery/computer/ship/attackby(obj/item/attacking_item, mob/user)
|
||||
if(attacking_item.tool_behaviour == TOOL_CABLECOIL) // Repair from hotwire
|
||||
var/obj/item/stack/cable_coil/C = attacking_item
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// Powernet sensors are devices which relay information about connected powernet. This information may be relayed
|
||||
// via two procs. Proc return_reading_text will return fully HTML styled string which contains all information. This
|
||||
// may be used in PDAs or similar applications. Second proc, return_reading_data will return list containing needed data.
|
||||
// This is used in NanoUI, for example.
|
||||
|
||||
/obj/structure/machinery/power/sensor
|
||||
name = "Powernet Sensor"
|
||||
|
||||
@@ -70,4 +70,3 @@
|
||||
/obj/structure/machinery/alarm/monitor/isolation
|
||||
req_one_access = list(ACCESS_RESEARCH, ACCESS_ATMOSPHERICS, ACCESS_ENGINE_EQUIP)
|
||||
locked = 0
|
||||
remote_control = 1
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
## /TG/ Chat
|
||||
|
||||
/TG/ Chat, which will be referred to as TgChat from this point onwards, is a system in which we can send messages to clients in a controlled and semi-reliable manner. The standard way of sending messages to BYOND clients simply dumps whatever you output to them directly into their chat window, however BYOND allows us to load our own code on the client to change this behaviour in a way that allows us to do some pretty neat things.
|
||||
|
||||
### Message Format
|
||||
|
||||
TgChat handles sending messages from the server to the client through the use of JSON payloads, of which the format will change depending on the type of message and the intended client endpoint. An example of the payload for chat messages is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"sequence": 0,
|
||||
"content": {
|
||||
"type": ". . .", // ?optional
|
||||
"text": ". . .", // ?optional !atleast-one
|
||||
"html": ". . .", // ?optional !atleast-one
|
||||
"avoidHighlighting": 0 // ?optional
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Reliability
|
||||
|
||||
In the past there have been issues where BYOND will silently and without reason lose a message we sent to the client, to detect this and recover from it seamlessly TgChat also has a baked in reliability layer. This reliability layer is very primitive, and simply keeps track of received sequence numbers. Should the client receive an unexpected sequence number TgChat asks the server to resend any missing packets.
|
||||
|
||||
### Ping System
|
||||
|
||||
TgChat supports a round trip time ping measurement, which is displayed to the client so they can know how long it takes for their commands and inputs to reach the server. This is done by sending the client a ping request, `ping/soft`, which tells the client to send a ping to the server. When the server receives said ping it sends a reply, `ping/reply`, to the client with a payload containing the current DateTime which the client can reference against the initial ping request.
|
||||
|
||||
### Chat Tabs, Local Storage, and Highlighting
|
||||
|
||||
To make organizing and managing chat easier and more functional for both players and admins, TgChat has the ability to filter out messages based on their primary tag, such as individual departmental radios, to a dedicated chat tab for easier reading and comprehension. These tabs can also be configured to highlist messages based on a simple keyword search. You can set a multitude of different keywords to search for and they will be highlighting for instant alerting of the client. Said tabs, highlighting rules, and your chat history will persist thanks to use of local storage on the client. Using local storage TgChat can ensure that your preferences are saved and maintained between client restarts and switching between other /TG/ servers. Local Storage is also used to keep your chat history aswell, should you need to scroll through your chat logs.
|
||||
@@ -35,23 +35,8 @@
|
||||
if(text) message["text"] = text
|
||||
if(html) message["html"] = html
|
||||
if(avoid_highlighting) message["avoidHighlighting"] = avoid_highlighting
|
||||
var/message_blob = TGUI_CREATE_MESSAGE("chat/message", message)
|
||||
var/message_html = message_to_html(message)
|
||||
if(islist(target))
|
||||
for(var/_target in target)
|
||||
var/client/client = CLIENT_FROM_VAR(_target)
|
||||
if(client)
|
||||
// Send to tgchat
|
||||
client.tgui_panel?.window.send_raw_message(message_blob)
|
||||
// Send to old chat
|
||||
to_target(client, message_html)
|
||||
return
|
||||
var/client/client = CLIENT_FROM_VAR(target)
|
||||
if(client)
|
||||
// Send to tgchat
|
||||
client.tgui_panel?.window.send_raw_message(message_blob)
|
||||
// Send to old chat
|
||||
to_target(client, message_html)
|
||||
// send it immediately
|
||||
SSchat.send_immediate(target, message)
|
||||
|
||||
/**
|
||||
* Sends the message to the recipient (target).
|
||||
@@ -74,7 +59,7 @@
|
||||
trailing_newline = TRUE,
|
||||
confidential = FALSE
|
||||
)
|
||||
if(isnull(Master) || !SSchat?.initialized)
|
||||
if(isnull(Master) || !SSchat?.initialized || !MC_RUNNING(SSchat.init_stage))
|
||||
to_chat_immediate(target, html, type, text, avoid_highlighting)
|
||||
return
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2017-2023 Jordan Brown
|
||||
Copyright (c) 2017-2024 Jordan Brown
|
||||
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# DMAPI Internals
|
||||
|
||||
This folder should be placed on it's own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
|
||||
This folder should be placed on its own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
|
||||
|
||||
- [includes.dm](./includes.dm) is the file that should be included by DM code, it handles including the rest.
|
||||
- The [core](./core) folder includes all code not directly part of any API version.
|
||||
- The other versioned folders contain code for the different DMAPI versions.
|
||||
- [v3210](./v3210) contains the final TGS3 API.
|
||||
- [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
|
||||
- [v5](./v5) is the current DMAPI version used by TGS >=4.1.
|
||||
- [v3210](./v3210) contains the final TGS3 API.
|
||||
- [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
|
||||
- [v5](./v5) is the current DMAPI version used by TGS >=4.1.
|
||||
- [LICENSE](./LICENSE) is the MIT license for the DMAPI.
|
||||
|
||||
APIs communicate with TGS in two ways. All versions implement TGS -> DM communication using /world/Topic. DM -> TGS communication, called the bridge method, is different for each version.
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This folder contains all DMAPI code not directly involved in an API.
|
||||
|
||||
- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
|
||||
- [\_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
|
||||
- [byond_world_export.dm](./byond_world_export.dm) contains the default `/datum/tgs_http_handler` implementation which uses `world.Export()`.
|
||||
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
|
||||
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
|
||||
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
|
||||
-
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/datum/tgs_http_handler/byond_world_export
|
||||
|
||||
/datum/tgs_http_handler/byond_world_export/PerformGet(url)
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export(url)
|
||||
TGS_DEBUG_LOG("byond_world_export: Export complete")
|
||||
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request: [url]")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
var/content = export_response["CONTENT"]
|
||||
if(!content)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request, missing content!")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
var/response_json = TGS_FILE2TEXT_NATIVE(content)
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("byond_world_export: Failed request, failed to load content!")
|
||||
return new /datum/tgs_http_result(null, FALSE)
|
||||
|
||||
return new /datum/tgs_http_result(response_json, TRUE)
|
||||
@@ -1,7 +1,7 @@
|
||||
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
|
||||
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE, datum/tgs_http_handler/http_handler = null)
|
||||
var/current_api = TGS_READ_GLOBAL(tgs)
|
||||
if(current_api)
|
||||
TGS_ERROR_LOG("API datum already set ([REF(current_api)] ([current_api]))! Was TgsNew() called more than once?")
|
||||
TGS_ERROR_LOG("API datum already set ([current_api])! Was TgsNew() called more than once?")
|
||||
return
|
||||
|
||||
if(!(minimum_required_security_level in list(TGS_SECURITY_ULTRASAFE, TGS_SECURITY_SAFE, TGS_SECURITY_TRUSTED)))
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
var/datum/tgs_version/max_api_version = TgsMaximumApiVersion();
|
||||
if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
|
||||
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
|
||||
TGS_ERROR_LOG("Detected unknown Interop API version! Defaulting to latest. Update the DMAPI to fix this problem.")
|
||||
api_datum = /datum/tgs_api/latest
|
||||
|
||||
if(!api_datum)
|
||||
TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
|
||||
TGS_ERROR_LOG("Found unsupported Interop API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
|
||||
return
|
||||
|
||||
TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
|
||||
@@ -55,7 +55,10 @@
|
||||
TGS_ERROR_LOG("Invalid parameter for event_handler: [event_handler]")
|
||||
event_handler = null
|
||||
|
||||
var/datum/tgs_api/new_api = new api_datum(event_handler, version)
|
||||
if(!http_handler)
|
||||
http_handler = new /datum/tgs_http_handler/byond_world_export
|
||||
|
||||
var/datum/tgs_api/new_api = new api_datum(event_handler, version, http_handler)
|
||||
|
||||
TGS_WRITE_GLOBAL(tgs, new_api)
|
||||
|
||||
@@ -107,6 +110,13 @@
|
||||
if(api)
|
||||
return api.ApiVersion()
|
||||
|
||||
/world/TgsEngine()
|
||||
#ifdef OPENDREAM
|
||||
return TGS_ENGINE_TYPE_OPENDREAM
|
||||
#else
|
||||
return TGS_ENGINE_TYPE_BYOND
|
||||
#endif
|
||||
|
||||
/world/TgsInstanceName()
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
@@ -159,3 +169,11 @@
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
return api.Visibility()
|
||||
|
||||
/world/TgsTriggerEvent(event_name, list/parameters, wait_for_completion = FALSE)
|
||||
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
|
||||
if(api)
|
||||
if(!istype(parameters, /list))
|
||||
parameters = list()
|
||||
|
||||
return api.TriggerEvent(event_name, parameters, wait_for_completion)
|
||||
|
||||
@@ -6,8 +6,8 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
|
||||
|
||||
var/list/warned_deprecated_command_runs
|
||||
|
||||
/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version)
|
||||
. = ..()
|
||||
/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
|
||||
..()
|
||||
src.event_handler = event_handler
|
||||
src.version = version
|
||||
|
||||
@@ -17,7 +17,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
|
||||
world.sleep_offline = FALSE // https://www.byond.com/forum/post/2894866
|
||||
del(world)
|
||||
world.sleep_offline = FALSE // just in case, this is BYOND after all...
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
TGS_DEBUG_LOG("BYOND DIDN'T TERMINATE THE WORLD!!! TICK IS: [world.time], sleep_offline: [world.sleep_offline]")
|
||||
|
||||
/datum/tgs_api/latest
|
||||
@@ -69,3 +69,6 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
|
||||
|
||||
/datum/tgs_api/proc/Visibility()
|
||||
return TGS_UNIMPLEMENTED
|
||||
|
||||
/datum/tgs_api/proc/TriggerEvent(event_name, list/parameters, wait_for_completion)
|
||||
return FALSE
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/datum/tgs_version/New(raw_parameter)
|
||||
..()
|
||||
src.raw_parameter = raw_parameter
|
||||
deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
|
||||
var/list/version_bits = splittext(deprefixed_parameter, ".")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "core\_definitions.dm"
|
||||
#include "core\byond_world_export.dm"
|
||||
#include "core\core.dm"
|
||||
#include "core\datum.dm"
|
||||
#include "core\tgs_version.dm"
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
var/json = json_encode(data)
|
||||
|
||||
while(requesting_new_port && !override_requesting_new_port)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
//we need some port open at this point to facilitate return communication
|
||||
if(!world.port)
|
||||
@@ -209,7 +209,7 @@
|
||||
requesting_new_port = FALSE
|
||||
|
||||
while(export_lock)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
export_lock = TRUE
|
||||
|
||||
last_interop_response = null
|
||||
@@ -217,7 +217,7 @@
|
||||
text2file(json, server_commands_json_path)
|
||||
|
||||
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
if(!last_interop_response)
|
||||
TGS_ERROR_LOG("Failed to get export result for: [json]")
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no security restrictions.
|
||||
|
||||
- [__interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [\_\_interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS.
|
||||
- [\_defines.dm](./_defines.dm) contains constant definitions.
|
||||
- [api.dm](./api.dm) contains the bulk of the API code.
|
||||
- [bridge.dm](./bridge.dm) contains functions related to making bridge requests.
|
||||
- [chunking.dm](./chunking.dm) contains common function for splitting large raw data sets into chunks BYOND can natively process.
|
||||
|
||||
@@ -1 +1 @@
|
||||
"5.6.2"
|
||||
"5.10.1"
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
#define DMAPI5_TOPIC_REQUEST_LIMIT 65528
|
||||
#define DMAPI5_TOPIC_RESPONSE_LIMIT 65529
|
||||
|
||||
#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
|
||||
#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
|
||||
#define DMAPI5_BRIDGE_COMMAND_PRIME 2
|
||||
#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
|
||||
#define DMAPI5_BRIDGE_COMMAND_KILL 4
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
|
||||
#define DMAPI5_BRIDGE_COMMAND_CHUNK 6
|
||||
#define DMAPI5_BRIDGE_COMMAND_EVENT 7
|
||||
|
||||
#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
|
||||
#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
|
||||
#define DMAPI5_PARAMETER_TOPIC_PORT "topicPort"
|
||||
|
||||
#define DMAPI5_CHUNK "chunk"
|
||||
#define DMAPI5_CHUNK_PAYLOAD "payload"
|
||||
@@ -34,6 +35,7 @@
|
||||
#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
|
||||
#define DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION "eventInvocation"
|
||||
|
||||
#define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort"
|
||||
#define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation"
|
||||
@@ -80,6 +82,8 @@
|
||||
#define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8
|
||||
#define DMAPI5_TOPIC_COMMAND_SEND_CHUNK 9
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK 10
|
||||
#define DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST 11
|
||||
#define DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT 12
|
||||
|
||||
#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
|
||||
@@ -89,7 +93,9 @@
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName"
|
||||
#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate"
|
||||
#define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion"
|
||||
#define DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE "broadcastMessage"
|
||||
|
||||
#define DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT "clientCount"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse"
|
||||
#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage"
|
||||
#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
|
||||
@@ -114,3 +120,9 @@
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText"
|
||||
#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly"
|
||||
|
||||
#define DMAPI5_EVENT_ID "eventId"
|
||||
|
||||
#define DMAPI5_EVENT_INVOCATION_NAME "eventName"
|
||||
#define DMAPI5_EVENT_INVOCATION_PARAMETERS "parameters"
|
||||
#define DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION "notifyCompletion"
|
||||
|
||||
+96
-30
@@ -8,8 +8,12 @@
|
||||
|
||||
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/intercepted_message_queue
|
||||
|
||||
/// List of chat messages list()s that attempted to be sent during a topic call. To be bundled in the result of the call
|
||||
var/list/offline_message_queue
|
||||
|
||||
var/list/custom_commands
|
||||
|
||||
var/list/test_merges
|
||||
@@ -17,15 +21,23 @@
|
||||
var/list/chat_channels
|
||||
|
||||
var/initialized = FALSE
|
||||
var/initial_bridge_request_received = FALSE
|
||||
var/datum/tgs_version/interop_version
|
||||
|
||||
var/chunked_requests = 0
|
||||
var/list/chunked_topics = list()
|
||||
|
||||
var/list/pending_events = list()
|
||||
|
||||
var/detached = FALSE
|
||||
|
||||
/datum/tgs_api/v5/New()
|
||||
var/datum/tgs_http_handler/http_handler
|
||||
|
||||
/datum/tgs_api/v5/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version, datum/tgs_http_handler/http_handler)
|
||||
. = ..()
|
||||
TGS_DEBUG_LOG("V5 API created")
|
||||
interop_version = version
|
||||
src.http_handler = http_handler
|
||||
TGS_DEBUG_LOG("V5 API created: [json_encode(args)]")
|
||||
|
||||
/datum/tgs_api/v5/ApiVersion()
|
||||
return new /datum/tgs_version(
|
||||
@@ -38,8 +50,14 @@
|
||||
access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
|
||||
|
||||
var/datum/tgs_version/api_version = ApiVersion()
|
||||
version = null
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
|
||||
version = null // we want this to be the TGS version, not the interop version
|
||||
|
||||
// sleep once to prevent an issue where world.Export on the first tick can hang indefinitely
|
||||
TGS_DEBUG_LOG("Starting Export bug prevention sleep tick. time:[world.time] sleep_offline:[world.sleep_offline]")
|
||||
sleep(world.tick_lag)
|
||||
TGS_DEBUG_LOG("Export bug prevention sleep complete")
|
||||
|
||||
var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands(), DMAPI5_PARAMETER_TOPIC_PORT = GetTopicPort()))
|
||||
if(!istype(bridge_response))
|
||||
TGS_ERROR_LOG("Failed initial bridge request!")
|
||||
return FALSE
|
||||
@@ -53,7 +71,8 @@
|
||||
TGS_INFO_LOG("DMAPI validation, exiting...")
|
||||
TerminateWorld()
|
||||
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION])
|
||||
initial_bridge_request_received = TRUE
|
||||
version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION]) // reassigning this because it can change if TGS updates
|
||||
security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL]
|
||||
visibility = runtime_information[DMAPI5_RUNTIME_INFORMATION_VISIBILITY]
|
||||
instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME]
|
||||
@@ -78,7 +97,7 @@
|
||||
if(revInfo)
|
||||
tm.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
|
||||
tm.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
|
||||
tm.timestamp = entry[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
|
||||
tm.timestamp = revisionData[DMAPI5_REVISION_INFORMATION_TIMESTAMP]
|
||||
else
|
||||
TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!")
|
||||
|
||||
@@ -102,15 +121,22 @@
|
||||
initialized = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/GetTopicPort()
|
||||
#if defined(OPENDREAM) && defined(OPENDREAM_TOPIC_PORT_EXISTS)
|
||||
return "[world.opendream_topic_port]"
|
||||
#else
|
||||
return null
|
||||
#endif
|
||||
|
||||
/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse()")
|
||||
var/logged = FALSE
|
||||
while(!version)
|
||||
while(!initial_bridge_request_received)
|
||||
if(!logged)
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Starting sleep")
|
||||
logged = TRUE
|
||||
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("RequireInitialBridgeResponse: Passed")
|
||||
|
||||
@@ -183,17 +209,7 @@
|
||||
var/datum/tgs_chat_channel/channel = I
|
||||
ids += channel.id
|
||||
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
|
||||
if (!length(channels))
|
||||
return
|
||||
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
SendChatMessageRaw(message2, ids)
|
||||
|
||||
/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message2, admin_only)
|
||||
var/list/channels = list()
|
||||
@@ -202,32 +218,82 @@
|
||||
if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
|
||||
channels += channel.id
|
||||
|
||||
SendChatMessageRaw(message2, channels)
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
|
||||
SendChatMessageRaw(message2, list(user.channel.id))
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatMessageRaw(datum/tgs_message_content/message2, list/channel_ids)
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
|
||||
if (!length(channels))
|
||||
if (!length(channel_ids))
|
||||
return
|
||||
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channel_ids
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
return
|
||||
|
||||
/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message2, datum/tgs_chat_user/user)
|
||||
message2 = UpgradeDeprecatedChatMessage(message2)
|
||||
var/list/data = message2._interop_serialize()
|
||||
data[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id)
|
||||
if(intercepted_message_queue)
|
||||
intercepted_message_queue += list(data)
|
||||
if(offline_message_queue)
|
||||
offline_message_queue += list(data)
|
||||
return
|
||||
|
||||
if(detached)
|
||||
offline_message_queue = list(data)
|
||||
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
data = offline_message_queue
|
||||
offline_message_queue = null
|
||||
|
||||
for(var/queued_message in data)
|
||||
SendChatDataRaw(queued_message)
|
||||
else
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
SendChatDataRaw(data)
|
||||
|
||||
/datum/tgs_api/v5/proc/SendChatDataRaw(list/data)
|
||||
Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = data))
|
||||
|
||||
/datum/tgs_api/v5/ChatChannelInfo()
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
return chat_channels.Copy()
|
||||
|
||||
/datum/tgs_api/v5/TriggerEvent(event_name, list/parameters, wait_for_completion)
|
||||
RequireInitialBridgeResponse()
|
||||
WaitForReattach(TRUE)
|
||||
|
||||
if(interop_version.minor < 9)
|
||||
TGS_WARNING_LOG("Interop version too low for custom events!")
|
||||
return FALSE
|
||||
|
||||
var/str_parameters = list()
|
||||
for(var/i in parameters)
|
||||
str_parameters += "[i]"
|
||||
|
||||
var/list/response = Bridge(DMAPI5_BRIDGE_COMMAND_EVENT, list(DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION = list(DMAPI5_EVENT_INVOCATION_NAME = event_name, DMAPI5_EVENT_INVOCATION_PARAMETERS = str_parameters, DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION = wait_for_completion)))
|
||||
if(!response)
|
||||
return FALSE
|
||||
|
||||
var/event_id = response[DMAPI5_EVENT_ID]
|
||||
if(!event_id)
|
||||
return FALSE
|
||||
|
||||
TGS_DEBUG_LOG("Created event ID: [event_id]")
|
||||
if(!wait_for_completion)
|
||||
return TRUE
|
||||
|
||||
TGS_DEBUG_LOG("Waiting for completion of event ID: [event_id]")
|
||||
|
||||
while(!pending_events[event_id])
|
||||
sleep(world.tick_lag)
|
||||
|
||||
TGS_DEBUG_LOG("Completed wait on event ID: [event_id]")
|
||||
pending_events -= event_id
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
|
||||
TGS_DEBUG_LOG("DecodeChannels()")
|
||||
var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
|
||||
|
||||
@@ -48,7 +48,9 @@
|
||||
var/json = CreateBridgeData(command, data, TRUE)
|
||||
var/encoded_json = url_encode(json)
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
var/api_prefix = interop_version.minor >= 8 ? "api/" : ""
|
||||
|
||||
var/url = "http://127.0.0.1:[server_port]/[api_prefix]Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]"
|
||||
return url
|
||||
|
||||
/datum/tgs_api/v5/proc/CreateBridgeData(command, list/data, needs_auth)
|
||||
@@ -63,7 +65,7 @@
|
||||
if(detached)
|
||||
// Wait up to one minute
|
||||
for(var/i in 1 to 600)
|
||||
sleep(1)
|
||||
sleep(world.tick_lag)
|
||||
if(!detached && (!require_channels || length(chat_channels)))
|
||||
break
|
||||
|
||||
@@ -75,20 +77,25 @@
|
||||
/datum/tgs_api/v5/proc/PerformBridgeRequest(bridge_request)
|
||||
WaitForReattach(FALSE)
|
||||
|
||||
// This is an infinite sleep until we get a response
|
||||
var/export_response = world.Export(bridge_request)
|
||||
if(!export_response)
|
||||
TGS_ERROR_LOG("Failed bridge request: [bridge_request]")
|
||||
TGS_DEBUG_LOG("Bridge request start")
|
||||
var/datum/tgs_http_result/result = http_handler.PerformGet(bridge_request)
|
||||
TGS_DEBUG_LOG("Bridge request complete")
|
||||
|
||||
if(isnull(result))
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned null!")
|
||||
return
|
||||
|
||||
var/response_json = file2text(export_response["CONTENT"])
|
||||
if(!response_json)
|
||||
TGS_ERROR_LOG("Failed bridge request, missing content!")
|
||||
if(!istype(result) || result.type != /datum/tgs_http_result)
|
||||
TGS_ERROR_LOG("Failed bridge request, handler returned non-[/datum/tgs_http_result]!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(response_json)
|
||||
if(!result.success)
|
||||
TGS_DEBUG_LOG("Failed bridge request, HTTP request failed!")
|
||||
return
|
||||
|
||||
var/list/bridge_response = json_decode(result.response_text)
|
||||
if(!bridge_response)
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [response_json]")
|
||||
TGS_ERROR_LOG("Failed bridge request, bad json: [result.response_text]")
|
||||
return
|
||||
|
||||
var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
|
||||
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
||||
|
||||
if(event_handler != null)
|
||||
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
|
||||
@@ -141,7 +141,7 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE)
|
||||
var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]")
|
||||
|
||||
server_port = new_port
|
||||
return TopicResponse()
|
||||
@@ -149,7 +149,9 @@
|
||||
if(DMAPI5_TOPIC_COMMAND_HEALTHCHECK)
|
||||
if(event_handler && event_handler.receive_health_checks)
|
||||
event_handler.HandleEvent(TGS_EVENT_HEALTH_CHECK)
|
||||
return TopicResponse()
|
||||
var/list/health_check_response = TopicResponse()
|
||||
health_check_response[DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT] = TGS_CLIENT_COUNT
|
||||
return health_check_response;
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH)
|
||||
detached = FALSE
|
||||
@@ -157,7 +159,7 @@
|
||||
var/error_message = null
|
||||
if (new_port != null)
|
||||
if (!isnum(new_port) || !(new_port > 0))
|
||||
error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]"
|
||||
error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]"
|
||||
else
|
||||
server_port = new_port
|
||||
|
||||
@@ -165,7 +167,7 @@
|
||||
if (!istext(new_version_string))
|
||||
if(error_message != null)
|
||||
error_message += ", "
|
||||
error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]"
|
||||
error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]"
|
||||
else
|
||||
var/datum/tgs_version/new_version = new(new_version_string)
|
||||
if (event_handler)
|
||||
@@ -175,6 +177,11 @@
|
||||
|
||||
var/list/reattach_response = TopicResponse(error_message)
|
||||
reattach_response[DMAPI5_PARAMETER_CUSTOM_COMMANDS] = ListCustomCommands()
|
||||
reattach_response[DMAPI5_PARAMETER_TOPIC_PORT] = GetTopicPort()
|
||||
|
||||
for(var/eventId in pending_events)
|
||||
pending_events[eventId] = TRUE
|
||||
|
||||
return reattach_response
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_SEND_CHUNK)
|
||||
@@ -267,4 +274,25 @@
|
||||
|
||||
return chunk_to_send
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST)
|
||||
var/message = topic_parameters[DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]
|
||||
if (!istext(message))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE]")
|
||||
|
||||
TGS_WORLD_ANNOUNCE(message)
|
||||
return TopicResponse()
|
||||
|
||||
if(DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT)
|
||||
var/event_id = topic_parameters[DMAPI5_EVENT_ID]
|
||||
if (!istext(event_id))
|
||||
return TopicResponse("Invalid or missing [DMAPI5_EVENT_ID]")
|
||||
|
||||
TGS_DEBUG_LOG("Completing event ID [event_id]...")
|
||||
pending_events[event_id] = TRUE
|
||||
return TopicResponse()
|
||||
|
||||
return TopicResponse("Unknown command: [command]")
|
||||
|
||||
/datum/tgs_api/v5/proc/WorldBroadcast(message)
|
||||
set waitfor = FALSE
|
||||
TGS_WORLD_ANNOUNCE(message)
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
#undef DMAPI5_TOPIC_REQUEST_LIMIT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_LIMIT
|
||||
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
|
||||
#undef DMAPI5_BRIDGE_COMMAND_STARTUP
|
||||
#undef DMAPI5_BRIDGE_COMMAND_PRIME
|
||||
#undef DMAPI5_BRIDGE_COMMAND_REBOOT
|
||||
#undef DMAPI5_BRIDGE_COMMAND_KILL
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND
|
||||
#undef DMAPI5_BRIDGE_COMMAND_CHUNK
|
||||
#undef DMAPI5_BRIDGE_COMMAND_EVENT
|
||||
|
||||
#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
|
||||
#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
|
||||
#undef DMAPI5_PARAMETER_TOPIC_PORT
|
||||
|
||||
#undef DMAPI5_CHUNK
|
||||
#undef DMAPI5_CHUNK_PAYLOAD
|
||||
@@ -34,6 +35,7 @@
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_VERSION
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL
|
||||
#undef DMAPI5_BRIDGE_PARAMETER_EVENT_INVOCATION
|
||||
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT
|
||||
#undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION
|
||||
@@ -78,6 +80,10 @@
|
||||
#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE
|
||||
#undef DMAPI5_TOPIC_COMMAND_HEALTHCHECK
|
||||
#undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH
|
||||
#undef DMAPI5_TOPIC_COMMAND_SEND_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_CHUNK
|
||||
#undef DMAPI5_TOPIC_COMMAND_RECEIVE_BROADCAST
|
||||
#undef DMAPI5_TOPIC_COMMAND_COMPLETE_EVENT
|
||||
|
||||
#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
|
||||
@@ -87,7 +93,9 @@
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME
|
||||
#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE
|
||||
#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION
|
||||
#undef DMAPI5_TOPIC_PARAMETER_BROADCAST_MESSAGE
|
||||
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CLIENT_COUNT
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE
|
||||
#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
|
||||
@@ -112,3 +120,9 @@
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT
|
||||
#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY
|
||||
|
||||
#undef DMAPI5_EVENT_ID
|
||||
|
||||
#undef DMAPI5_EVENT_INVOCATION_NAME
|
||||
#undef DMAPI5_EVENT_INVOCATION_PARAMETERS
|
||||
#undef DMAPI5_EVENT_INVOCATION_NOTIFY_COMPLETION
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
*
|
||||
* required user mob The mob interacting with the UI.
|
||||
*
|
||||
* return list Statuic Data to be sent to the UI.
|
||||
* return list Static Data to be sent to the UI.
|
||||
*/
|
||||
/datum/proc/ui_static_data(mob/user)
|
||||
return list()
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
// Disable UIs if unconscious.
|
||||
else if(stat)
|
||||
return UI_DISABLED
|
||||
// Update UIs if incapicitated but concious.
|
||||
// Update UIs if incapicitated but conscious.
|
||||
else if(incapacitated())
|
||||
return UI_UPDATE
|
||||
return UI_INTERACTIVE
|
||||
@@ -129,6 +129,3 @@
|
||||
|
||||
/datum/ui_state/proc/href_list(mob/user)
|
||||
return list()
|
||||
|
||||
/datum/proc/nano_container()
|
||||
return src
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
|
||||
|
||||
/datum/ui_state/default/can_use_topic(src_object, mob/user)
|
||||
return user.default_can_use_topic(src_object) // Call the individual mob-overridden procs.
|
||||
return user?.default_can_use_topic(src_object) // Call the individual mob-overridden procs.
|
||||
|
||||
/mob/proc/default_can_use_topic(src_object)
|
||||
return UI_CLOSE // Don't allow interaction by default.
|
||||
@@ -41,8 +41,11 @@ GLOBAL_DATUM_INIT(default_state, /datum/ui_state/default, new)
|
||||
if(. < UI_INTERACTIVE)
|
||||
return
|
||||
|
||||
if(src_object == src)
|
||||
return UI_INTERACTIVE
|
||||
|
||||
// The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
|
||||
if(!control_disabled)
|
||||
if(!control_disabled && can_see(src_object))
|
||||
return UI_INTERACTIVE
|
||||
return UI_CLOSE
|
||||
|
||||
|
||||
@@ -83,6 +83,9 @@
|
||||
return UI_DISABLED // Otherwise they can keep the UI open.
|
||||
|
||||
/mob/living/silicon/ai/get_ui_access(atom/source)
|
||||
if(source == src)
|
||||
return UI_INTERACTIVE
|
||||
|
||||
// The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
|
||||
if(!control_disabled && can_see(source))
|
||||
return UI_INTERACTIVE
|
||||
@@ -113,3 +116,11 @@
|
||||
return UI_CLOSE
|
||||
|
||||
return UI_INTERACTIVE
|
||||
|
||||
/// Return UI_INTERACTIVE if the user is inside the target atom, whether they can see it or not.
|
||||
/// Return UI_CLOSE otherwise.
|
||||
/proc/ui_status_user_inside(mob/user, atom/target)
|
||||
if(target.contains(user))
|
||||
return UI_INTERACTIVE
|
||||
|
||||
return UI_CLOSE
|
||||
|
||||
+35
-51
@@ -37,11 +37,9 @@
|
||||
var/datum/ui_state/state = null
|
||||
/// Rate limit client refreshes to prevent DoS.
|
||||
COOLDOWN_DECLARE(refresh_cooldown)
|
||||
/// Are byond mouse events beyond the window passed in to the ui
|
||||
var/mouse_hooked = FALSE
|
||||
|
||||
/// Any partial packets that we have received from TGUI, waiting to be sent
|
||||
var/partial_packets
|
||||
/// The id of any ByondUi elements that we have opened
|
||||
var/list/open_byondui_elements
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -58,9 +56,6 @@
|
||||
* return datum/tgui The requested UI.
|
||||
*/
|
||||
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
|
||||
log_tgui(user,
|
||||
"new [interface] fancy [user?.client?.prefs.tgui_fancy]",
|
||||
src_object = src_object)
|
||||
src.user = user
|
||||
src.src_object = src_object
|
||||
src.window_key = "[REF(src_object)]-main"
|
||||
@@ -85,7 +80,7 @@
|
||||
* return bool - TRUE if a new pooled window is opened, FALSE in all other situations including if a new pooled window didn't open because one already exists.
|
||||
*/
|
||||
/datum/tgui/proc/open()
|
||||
if(!user?.client)
|
||||
if(user && !user.client)
|
||||
return FALSE
|
||||
if(window)
|
||||
return FALSE
|
||||
@@ -100,7 +95,6 @@
|
||||
if(!window.is_ready())
|
||||
window.initialize(
|
||||
strict_mode = TRUE,
|
||||
fancy = user.client.prefs.tgui_fancy,
|
||||
assets = list(
|
||||
get_asset_datum(/datum/asset/simple/tgui),
|
||||
))
|
||||
@@ -110,8 +104,6 @@
|
||||
window.send_message("update", get_payload(
|
||||
with_data = TRUE,
|
||||
with_static_data = TRUE))
|
||||
if(mouse_hooked)
|
||||
window.set_mouse_macro()
|
||||
SStgui.on_open(src)
|
||||
|
||||
return TRUE
|
||||
@@ -146,11 +138,27 @@
|
||||
window.release_lock()
|
||||
window.close(can_be_suspended)
|
||||
src_object.ui_close(user)
|
||||
SEND_SIGNAL(src, COMSIG_TGUI_CLOSE, user)
|
||||
SStgui.on_close(src)
|
||||
|
||||
if(user.client)
|
||||
terminate_byondui_elements()
|
||||
|
||||
state = null
|
||||
qdel(src)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Closes all ByondUI elements, left dangling by a forceful TGUI exit,
|
||||
* such as via Alt+F4, or terminating the process
|
||||
*
|
||||
*/
|
||||
/datum/tgui/proc/terminate_byondui_elements()
|
||||
set waitfor = FALSE
|
||||
|
||||
for(var/byondui_element in open_byondui_elements)
|
||||
winset(user.client, byondui_element, list("parent" = ""))
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -161,18 +169,6 @@
|
||||
/datum/tgui/proc/set_autoupdate(autoupdate)
|
||||
src.autoupdate = autoupdate
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable passing through byond mouse events to the window
|
||||
*
|
||||
* required value bool Enable/disable hooking.
|
||||
*/
|
||||
/datum/tgui/proc/set_mouse_hook(value)
|
||||
src.mouse_hooked = value
|
||||
//Handle unhooking/hooking on already open windows ?
|
||||
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -248,12 +244,13 @@
|
||||
json_data["config"] = list(
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"interface" = interface,
|
||||
"interface" = list(
|
||||
"name" = interface,
|
||||
),
|
||||
"refreshing" = refreshing,
|
||||
"window" = list(
|
||||
"key" = window_key,
|
||||
"size" = window_size,
|
||||
"fancy" = user.client.prefs.tgui_fancy,
|
||||
"locked" = user.client.prefs.tgui_lock,
|
||||
"scale" = user.client.prefs.ui_scale,
|
||||
),
|
||||
@@ -329,31 +326,6 @@
|
||||
// Pass act type messages to ui_act
|
||||
if(type && copytext(type, 1, 5) == "act/")
|
||||
var/act_type = copytext(type, 5)
|
||||
|
||||
var/id = href_list["packetId"]
|
||||
if(!isnull(id))
|
||||
id = text2num(id)
|
||||
|
||||
var/total = text2num(href_list["totalPackets"])
|
||||
|
||||
if(total > MAX_MESSAGE_CHUNKS)
|
||||
return
|
||||
|
||||
if(id == 1)
|
||||
partial_packets = new /list(total)
|
||||
|
||||
partial_packets[id] = href_list["packet"]
|
||||
|
||||
if(id != total)
|
||||
return
|
||||
|
||||
var/assembled_payload = ""
|
||||
for(var/packet in partial_packets)
|
||||
assembled_payload += packet
|
||||
|
||||
payload = json_decode(assembled_payload)
|
||||
partial_packets = null
|
||||
|
||||
log_tgui(user, "Action: [act_type] [href_list["payload"]]",
|
||||
window = window,
|
||||
src_object = src_object)
|
||||
@@ -381,6 +353,18 @@
|
||||
LAZYINITLIST(src_object.tgui_shared_states)
|
||||
src_object.tgui_shared_states[href_list["key"]] = href_list["value"]
|
||||
SStgui.update_uis(src_object)
|
||||
if(TGUI_MANAGED_BYONDUI_TYPE_RENDER)
|
||||
var/byond_ui_id = payload[TGUI_MANAGED_BYONDUI_PAYLOAD_ID]
|
||||
if(!byond_ui_id || LAZYLEN(open_byondui_elements) > TGUI_MANAGED_BYONDUI_LIMIT)
|
||||
return
|
||||
|
||||
LAZYOR(open_byondui_elements, byond_ui_id)
|
||||
if(TGUI_MANAGED_BYONDUI_TYPE_UNMOUNT)
|
||||
var/byond_ui_id = payload[TGUI_MANAGED_BYONDUI_PAYLOAD_ID]
|
||||
if(!byond_ui_id)
|
||||
return
|
||||
|
||||
LAZYREMOVE(open_byondui_elements, byond_ui_id)
|
||||
|
||||
/// Wrapper for behavior to potentially wait until the next tick if the server is overloaded
|
||||
/datum/tgui/proc/on_act_message(act_type, payload, state)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
var/is_browser = FALSE
|
||||
var/status = TGUI_WINDOW_CLOSED
|
||||
var/locked = FALSE
|
||||
var/visible = FALSE
|
||||
var/datum/tgui/locked_by
|
||||
var/datum/subscriber_object
|
||||
var/subscriber_delegate
|
||||
@@ -19,12 +20,12 @@
|
||||
var/sent_assets = list()
|
||||
// Vars passed to initialize proc (and saved for later)
|
||||
var/initial_strict_mode
|
||||
var/initial_fancy
|
||||
var/initial_assets
|
||||
var/initial_inline_html
|
||||
var/initial_inline_js
|
||||
var/initial_inline_css
|
||||
var/mouse_event_macro_set = FALSE
|
||||
|
||||
var/list/oversized_payloads = list()
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -50,7 +51,6 @@
|
||||
* will be put into the queue until the window finishes loading.
|
||||
*
|
||||
* optional strict_mode bool - Enables strict error handling and BSOD.
|
||||
* optional fancy bool - If TRUE and if this is NOT a panel, will hide the window titlebar.
|
||||
* optional assets list - List of assets to load during initialization.
|
||||
* optional inline_html string - Custom HTML to inject.
|
||||
* optional inline_js string - Custom JS to inject.
|
||||
@@ -58,7 +58,6 @@
|
||||
*/
|
||||
/datum/tgui_window/proc/initialize(
|
||||
strict_mode = FALSE,
|
||||
fancy = FALSE,
|
||||
assets = list(),
|
||||
inline_html = "",
|
||||
inline_js = "",
|
||||
@@ -68,7 +67,6 @@
|
||||
window = src)
|
||||
if(!client)
|
||||
return
|
||||
src.initial_fancy = fancy
|
||||
src.initial_assets = assets
|
||||
src.initial_inline_html = inline_html
|
||||
src.initial_inline_js = inline_js
|
||||
@@ -76,16 +74,12 @@
|
||||
status = TGUI_WINDOW_LOADING
|
||||
fatally_errored = FALSE
|
||||
// Build window options
|
||||
var/options = "file=[id].html;can_minimize=0;auto_format=0;"
|
||||
// Remove titlebar and resize handles for a fancy window
|
||||
if(fancy)
|
||||
options += "titlebar=0;can_resize=0;"
|
||||
else
|
||||
options += "titlebar=1;can_resize=1;"
|
||||
var/options = "file=[id].html;can_minimize=0;auto_format=0;titlebar=0;can_resize=0;"
|
||||
// Generate page html
|
||||
var/html = SStgui.basehtml
|
||||
html = replacetextEx(html, "\[tgui:windowId]", id)
|
||||
html = replacetextEx(html, "\[tgui:strictMode]", strict_mode)
|
||||
html = replacetextEx(html, "\[tgui:storagecdn]", GLOB.config.storage_cdn_iframe)
|
||||
// Inject assets
|
||||
var/inline_assets_str = ""
|
||||
for(var/datum/asset/asset in assets)
|
||||
@@ -128,7 +122,6 @@
|
||||
/datum/tgui_window/proc/reinitialize()
|
||||
initialize(
|
||||
strict_mode = initial_strict_mode,
|
||||
fancy = initial_fancy,
|
||||
assets = initial_assets,
|
||||
inline_html = initial_inline_html,
|
||||
inline_js = initial_inline_js,
|
||||
@@ -220,12 +213,11 @@
|
||||
/datum/tgui_window/proc/close(can_be_suspended = TRUE)
|
||||
if(!client)
|
||||
return
|
||||
if(mouse_event_macro_set)
|
||||
remove_mouse_macro()
|
||||
if(can_be_suspended && can_be_suspended())
|
||||
log_tgui(client,
|
||||
context = "[id]/close (suspending)",
|
||||
window = src)
|
||||
visible = FALSE
|
||||
status = TGUI_WINDOW_READY
|
||||
send_message("suspend")
|
||||
return
|
||||
@@ -233,6 +225,7 @@
|
||||
context = "[id]/close",
|
||||
window = src)
|
||||
release_lock()
|
||||
visible = FALSE
|
||||
status = TGUI_WINDOW_CLOSED
|
||||
message_queue = null
|
||||
// Do not close the window to give user some time
|
||||
@@ -298,9 +291,6 @@
|
||||
return
|
||||
sent_assets |= list(asset)
|
||||
. = asset.send(client)
|
||||
if(istype(asset, /datum/asset/spritesheet))
|
||||
var/datum/asset/spritesheet/spritesheet = asset
|
||||
send_message("asset/stylesheet", spritesheet.css_filename())
|
||||
send_raw_message(asset.get_serialized_url_mappings())
|
||||
|
||||
/**
|
||||
@@ -364,6 +354,11 @@
|
||||
switch(type)
|
||||
if("ping")
|
||||
send_message("ping/reply", payload)
|
||||
if("ping/set")
|
||||
client?.avgping = payload["ping"]
|
||||
if("visible")
|
||||
visible = TRUE
|
||||
SEND_SIGNAL(src, COMSIG_TGUI_WINDOW_VISIBLE, client)
|
||||
if("suspend")
|
||||
close(can_be_suspended = TRUE)
|
||||
if("close")
|
||||
@@ -372,40 +367,48 @@
|
||||
client << link(href_list["url"])
|
||||
if("cacheReloaded")
|
||||
reinitialize()
|
||||
if("chat/resend")
|
||||
SSchat.handle_resend(client, payload)
|
||||
if("oversizedPayloadRequest")
|
||||
var/payload_id = payload["id"]
|
||||
var/chunk_count = payload["chunkCount"]
|
||||
var/permit_payload = chunk_count <= 32
|
||||
if(permit_payload)
|
||||
create_oversized_payload(payload_id, payload["type"], chunk_count)
|
||||
send_message("oversizePayloadResponse", list("allow" = permit_payload, "id" = payload_id))
|
||||
if("payloadChunk")
|
||||
var/payload_id = payload["id"]
|
||||
append_payload_chunk(payload_id, payload["chunk"])
|
||||
send_message("acknowledgePayloadChunk", list("id" = payload_id))
|
||||
|
||||
/datum/tgui_window/proc/set_mouse_macro()
|
||||
if(mouse_event_macro_set)
|
||||
/datum/tgui_window/vv_edit_var(var_name, var_value)
|
||||
return var_name != NAMEOF(src, id) && ..()
|
||||
|
||||
/datum/tgui_window/proc/create_oversized_payload(payload_id, message_type, chunk_count)
|
||||
if(oversized_payloads[payload_id])
|
||||
stack_trace("Attempted to create oversized tgui payload with duplicate ID.")
|
||||
return
|
||||
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
oversized_payloads[payload_id] = list(
|
||||
"type" = message_type,
|
||||
"count" = chunk_count,
|
||||
"chunks" = list(),
|
||||
"timeout" = addtimer(CALLBACK(src, PROC_REF(remove_oversized_payload), payload_id), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
|
||||
)
|
||||
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
var/command_template = ".output CONTROL PAYLOAD"
|
||||
var/event_message = TGUI_CREATE_MESSAGE(byondToTguiEventMap[mouseMacro], null)
|
||||
var target_control = is_browser \
|
||||
? "[id]:update" \
|
||||
: "[id].browser:update"
|
||||
var/with_id = replacetext(command_template, "CONTROL", target_control)
|
||||
var/full_command = replacetext(with_id, "PAYLOAD", event_message)
|
||||
/datum/tgui_window/proc/append_payload_chunk(payload_id, chunk)
|
||||
var/list/payload = oversized_payloads[payload_id]
|
||||
if(!payload)
|
||||
return
|
||||
var/list/chunks = payload["chunks"]
|
||||
chunks += chunk
|
||||
if(length(chunks) >= payload["count"])
|
||||
deltimer(payload["timeout"])
|
||||
var/message_type = payload["type"]
|
||||
var/final_payload = chunks.Join()
|
||||
remove_oversized_payload(payload_id)
|
||||
on_message(message_type, json_decode(final_payload), list("type" = message_type, "payload" = final_payload, "tgui" = TRUE, "window_id" = id))
|
||||
else
|
||||
payload["timeout"] = addtimer(CALLBACK(src, PROC_REF(remove_oversized_payload), payload_id), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_STOPPABLE)
|
||||
|
||||
var/list/params = list()
|
||||
params["parent"] = "default" //Technically this is external to tgui but whatever
|
||||
params["name"] = mouseMacro
|
||||
params["command"] = full_command
|
||||
|
||||
winset(client, "[mouseMacro]Window[id]Macro", params)
|
||||
mouse_event_macro_set = TRUE
|
||||
|
||||
/datum/tgui_window/proc/remove_mouse_macro()
|
||||
if(!mouse_event_macro_set)
|
||||
stack_trace("Unsetting mouse macro on tgui window that has none")
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
)
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
winset(client, null, "[mouseMacro]Window[id]Macro.parent=null")
|
||||
mouse_event_macro_set = FALSE
|
||||
/datum/tgui_window/proc/remove_oversized_payload(payload_id)
|
||||
oversized_payloads -= payload_id
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* * timeout - The timeout of the alert, after which the modal will close and qdel itself. Set to zero for no timeout.
|
||||
* * autofocus - The bool that controls if this alert should grab window focus.
|
||||
*/
|
||||
/proc/tgui_alert(mob/user, message = "", title, list/buttons = list("Ok"), timeout = 0, autofocus = TRUE)
|
||||
/proc/tgui_alert(mob/user, message = "", title, list/buttons = list("Ok"), timeout = 0, autofocus = TRUE, ui_state = GLOB.always_state)
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
@@ -18,7 +18,11 @@
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
return null
|
||||
|
||||
if(isnull(user.client))
|
||||
return null
|
||||
|
||||
// A gentle nudge - you should not be using TGUI alert for anything other than a simple message.
|
||||
if(length(buttons) > 3)
|
||||
log_tgui(user, "Error: TGUI Alert initiated with too many buttons. Use a list.", "TguiAlert")
|
||||
@@ -29,7 +33,7 @@
|
||||
return alert(user, message, title, buttons[1], buttons[2])
|
||||
if(length(buttons) == 3)
|
||||
return alert(user, message, title, buttons[1], buttons[2], buttons[3])
|
||||
var/datum/tgui_alert/alert = new(user, message, title, buttons, timeout, autofocus)
|
||||
var/datum/tgui_alert/alert = new(user, message, title, buttons, timeout, autofocus, ui_state)
|
||||
alert.ui_interact(user)
|
||||
alert.wait()
|
||||
if (alert)
|
||||
@@ -59,19 +63,23 @@
|
||||
var/autofocus
|
||||
/// Boolean field describing if the tgui_alert was closed by the user.
|
||||
var/closed
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
|
||||
/datum/tgui_alert/New(mob/user, message, title, list/buttons, timeout, autofocus)
|
||||
/datum/tgui_alert/New(mob/user, message, title, list/buttons, timeout, autofocus, ui_state)
|
||||
src.autofocus = autofocus
|
||||
src.buttons = buttons.Copy()
|
||||
src.message = message
|
||||
src.title = title
|
||||
src.state = ui_state
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_alert/Destroy(force, ...)
|
||||
/datum/tgui_alert/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
buttons?.Cut()
|
||||
return ..()
|
||||
|
||||
@@ -94,7 +102,7 @@
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_alert/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
return state
|
||||
|
||||
/datum/tgui_alert/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
@@ -112,7 +120,7 @@
|
||||
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
return data
|
||||
|
||||
/datum/tgui_alert/ui_act(action, list/params)
|
||||
/datum/tgui_alert/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* ### tgui_input_checkbox
|
||||
* Opens a window with a list of checkboxes and returns a list of selected choices.
|
||||
*
|
||||
* user - The mob to display the window to
|
||||
* message - The message inside the window
|
||||
* title - The title of the window
|
||||
* list/items - The list of items to display
|
||||
* min_checked - The minimum number of checkboxes that must be checked (defaults to 1)
|
||||
* max_checked - The maximum number of checkboxes that can be checked (optional)
|
||||
* timeout - The timeout for the input (optional)
|
||||
*/
|
||||
/proc/tgui_input_checkboxes(mob/user, message, title = "Select", list/items, min_checked = 1, max_checked = 50, timeout = 0, ui_state = GLOB.always_state)
|
||||
if (!user)
|
||||
user = usr
|
||||
if(!length(items))
|
||||
return null
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return null
|
||||
|
||||
if(isnull(user.client))
|
||||
return null
|
||||
|
||||
if(!user.client.prefs.tgui_inputs)
|
||||
return input(user, message, title) as null|anything in items
|
||||
var/datum/tgui_checkbox_input/input = new(user, message, title, items, min_checked, max_checked, timeout, ui_state)
|
||||
input.ui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
. = input.choices
|
||||
qdel(input)
|
||||
|
||||
/// Window for tgui_input_checkboxes
|
||||
/datum/tgui_checkbox_input
|
||||
/// Title of the window
|
||||
var/title
|
||||
/// Message to display
|
||||
var/message
|
||||
/// List of items to display
|
||||
var/list/items
|
||||
/// List of selected items
|
||||
var/list/choices
|
||||
/// Time when the input was created
|
||||
var/start_time
|
||||
/// Timeout for the input
|
||||
var/timeout
|
||||
/// Whether the input was closed
|
||||
var/closed
|
||||
/// Minimum number of checkboxes that must be checked
|
||||
var/min_checked
|
||||
/// Maximum number of checkboxes that can be checked
|
||||
var/max_checked
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
|
||||
/datum/tgui_checkbox_input/New(mob/user, message, title, list/items, min_checked, max_checked, timeout, ui_state)
|
||||
src.title = title
|
||||
src.message = message
|
||||
src.items = items.Copy()
|
||||
src.min_checked = min_checked
|
||||
src.max_checked = max_checked
|
||||
src.state = ui_state
|
||||
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_checkbox_input/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
items?.Cut()
|
||||
return ..()
|
||||
|
||||
/datum/tgui_checkbox_input/proc/wait()
|
||||
while (!closed && !QDELETED(src))
|
||||
stoplag(1)
|
||||
|
||||
/datum/tgui_checkbox_input/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CheckboxInput")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_checkbox_input/ui_close(mob/user)
|
||||
. = ..()
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_checkbox_input/ui_state(mob/user)
|
||||
return state
|
||||
|
||||
/datum/tgui_checkbox_input/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
if(timeout)
|
||||
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_checkbox_input/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["items"] = items
|
||||
data["min_checked"] = min_checked
|
||||
data["max_checked"] = max_checked
|
||||
data["large_buttons"] = user.client.prefs.tgui_buttons_large
|
||||
data["message"] = message
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["title"] = title
|
||||
|
||||
return data
|
||||
|
||||
/datum/tgui_checkbox_input/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("submit")
|
||||
var/list/selections = params["entry"]
|
||||
if(length(selections) >= min_checked && length(selections) <= max_checked)
|
||||
set_choices(selections)
|
||||
closed = TRUE
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
|
||||
if("cancel")
|
||||
closed = TRUE
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/datum/tgui_checkbox_input/proc/set_choices(list/selections)
|
||||
src.choices = selections.Copy()
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Creates a TGUI window with a key input. Returns the user's response as a full key with modifiers, eg ShiftK.
|
||||
*
|
||||
* This proc should be used to create windows for key entry that the caller will wait for a response from.
|
||||
* If tgui fancy chat is turned off: Will return a normal input.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user to show the number input to.
|
||||
* * message - The content of the number input, shown in the body of the TGUI window.
|
||||
* * title - The title of the number input modal, shown on the top of the TGUI window.
|
||||
* * default - The default (or current) key, shown as a placeholder.
|
||||
*/
|
||||
/proc/tgui_input_keycombo(mob/user = usr, message, title = "Key Input", default = 0, timeout = 0, ui_state = GLOB.always_state)
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return null
|
||||
|
||||
if (isnull(user.client))
|
||||
return null
|
||||
|
||||
// Client does NOT have tgui_input on: Returns regular input
|
||||
if(!user.client.prefs.tgui_inputs)
|
||||
var/input_key = input(user, message, title + "(Modifiers are TGUI only, sorry!)", default) as null|text
|
||||
return input_key[1]
|
||||
var/datum/tgui_input_keycombo/key_input = new(user, message, title, default, timeout, ui_state)
|
||||
key_input.ui_interact(user)
|
||||
key_input.wait()
|
||||
if (key_input)
|
||||
. = key_input.entry
|
||||
qdel(key_input)
|
||||
|
||||
/**
|
||||
* # tgui_input_keycombo
|
||||
*
|
||||
* Datum used for instantiating and using a TGUI-controlled key input that prompts the user with
|
||||
* a message and listens for key presses.
|
||||
*/
|
||||
/datum/tgui_input_keycombo
|
||||
/// Boolean field describing if the tgui_input_number was closed by the user.
|
||||
var/closed
|
||||
/// The default (or current) value, shown as a default. Users can press reset with this.
|
||||
var/default
|
||||
/// The entry that the user has return_typed in.
|
||||
var/entry
|
||||
/// The prompt's body, if any, of the TGUI window.
|
||||
var/message
|
||||
/// The time at which the number input was created, for displaying timeout progress.
|
||||
var/start_time
|
||||
/// The lifespan of the number input, after which the window will close and delete itself.
|
||||
var/timeout
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
|
||||
/datum/tgui_input_keycombo/New(mob/user, message, title, default, timeout, ui_state)
|
||||
src.default = default
|
||||
src.message = message
|
||||
src.title = title
|
||||
src.state = ui_state
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_input_keycombo/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Waits for a user's response to the tgui_input_keycombo's prompt before returning. Returns early if
|
||||
* the window was closed by the user.
|
||||
*/
|
||||
/datum/tgui_input_keycombo/proc/wait()
|
||||
while (!entry && !closed && !QDELETED(src))
|
||||
stoplag(1)
|
||||
|
||||
/datum/tgui_input_keycombo/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "KeyComboModal")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_input_keycombo/ui_close(mob/user)
|
||||
. = ..()
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_input_keycombo/ui_state(mob/user)
|
||||
return state
|
||||
|
||||
/datum/tgui_input_keycombo/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["init_value"] = default // Default is a reserved keyword
|
||||
data["large_buttons"] = user.client.prefs.tgui_buttons_large
|
||||
data["message"] = message
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["title"] = title
|
||||
return data
|
||||
|
||||
/datum/tgui_input_keycombo/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
if(timeout)
|
||||
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
return data
|
||||
|
||||
/datum/tgui_input_keycombo/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
switch(action)
|
||||
if("submit")
|
||||
set_entry(params["entry"])
|
||||
closed = TRUE
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
if("cancel")
|
||||
closed = TRUE
|
||||
SStgui.close_uis(src)
|
||||
return TRUE
|
||||
|
||||
/datum/tgui_input_keycombo/proc/set_entry(entry)
|
||||
src.entry = entry
|
||||
@@ -10,21 +10,28 @@
|
||||
* * default - If an option is already preselected on the UI. Current values, etc.
|
||||
* * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout.
|
||||
*/
|
||||
/proc/tgui_input_list(mob/user, message, title = "Select", list/items, default, timeout = 0)
|
||||
/proc/tgui_input_list(mob/user, message, title = "Select", list/items, default, timeout = 0, ui_state = GLOB.always_state)
|
||||
if (!user)
|
||||
user = usr
|
||||
if(!length(items))
|
||||
return
|
||||
return null
|
||||
if (!istype(user))
|
||||
if (istype(user, /client))
|
||||
var/client/client = user
|
||||
user = client.mob
|
||||
else
|
||||
return
|
||||
return null
|
||||
|
||||
if(isnull(user.client))
|
||||
return null
|
||||
|
||||
/// Client does NOT have tgui_input on: Returns regular input
|
||||
if(!user.client.prefs.tgui_inputs)
|
||||
return input(user, message, title, default) as null|anything in items
|
||||
var/datum/tgui_list_input/input = new(user, message, title, items, default, timeout)
|
||||
var/datum/tgui_list_input/input = new(user, message, title, items, default, timeout, ui_state)
|
||||
if(input.invalid)
|
||||
qdel(input)
|
||||
return
|
||||
input.ui_interact(user)
|
||||
input.wait()
|
||||
if (input)
|
||||
@@ -56,13 +63,18 @@
|
||||
var/timeout
|
||||
/// Boolean field describing if the tgui_list_input was closed by the user.
|
||||
var/closed
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
/// Whether the tgui list input is invalid or not (i.e. due to all list entries being null)
|
||||
var/invalid = FALSE
|
||||
|
||||
/datum/tgui_list_input/New(mob/user, message, title, list/items, default, timeout)
|
||||
/datum/tgui_list_input/New(mob/user, message, title, list/items, default, timeout, ui_state)
|
||||
src.title = title
|
||||
src.message = message
|
||||
src.items = list()
|
||||
src.items_map = list()
|
||||
src.default = default
|
||||
src.state = ui_state
|
||||
var/list/repeat_items = list()
|
||||
// Gets rid of illegal characters
|
||||
var/static/regex/whitelistedWords = regex(@{"([^\u0020-\u8000]+)"})
|
||||
@@ -74,13 +86,17 @@
|
||||
string_key = avoid_assoc_duplicate_keys(string_key, repeat_items)
|
||||
src.items += string_key
|
||||
src.items_map[string_key] = i
|
||||
|
||||
if(length(src.items) == 0)
|
||||
invalid = TRUE
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_list_input/Destroy(force, ...)
|
||||
/datum/tgui_list_input/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
items?.Cut()
|
||||
items_map?.Cut()
|
||||
return ..()
|
||||
@@ -96,7 +112,7 @@
|
||||
/datum/tgui_list_input/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ListInputModal")
|
||||
ui = new(user, src, "ListInputWindow")
|
||||
ui.open()
|
||||
|
||||
/datum/tgui_list_input/ui_close(mob/user)
|
||||
@@ -104,15 +120,15 @@
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_list_input/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
return state
|
||||
|
||||
/datum/tgui_list_input/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["init_value"] = default || items[1]
|
||||
data["items"] = items
|
||||
data["large_buttons"] = user.client.prefs.tgui_buttons_large
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["message"] = message
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["title"] = title
|
||||
return data
|
||||
|
||||
@@ -122,7 +138,7 @@
|
||||
data["timeout"] = clamp((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS), 0, 1)
|
||||
return data
|
||||
|
||||
/datum/tgui_list_input/ui_act(action, list/params)
|
||||
/datum/tgui_list_input/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* * timeout - The timeout of the number input, after which the modal will close and qdel itself. Set to zero for no timeout.
|
||||
* * round_value - whether the inputted number is rounded down into an integer.
|
||||
*/
|
||||
/proc/tgui_input_number(mob/user, message, title = "Number Input", default = 0, max_value = 10000, min_value = 0, timeout = 0, round_value = TRUE)
|
||||
/proc/tgui_input_number(mob/user, message, title = "Number Input", default = 0, max_value = 10000, min_value = 0, timeout = 0, round_value = TRUE, ui_state = GLOB.always_state)
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
@@ -30,7 +30,7 @@
|
||||
if(!user.client.prefs.tgui_inputs)
|
||||
var/input_number = input(user, message, title, default) as null|num
|
||||
return clamp(round_value ? round(input_number) : input_number, min_value, max_value)
|
||||
var/datum/tgui_input_number/number_input = new(user, message, title, default, max_value, min_value, timeout, round_value)
|
||||
var/datum/tgui_input_number/number_input = new(user, message, title, default, max_value, min_value, timeout, round_value, ui_state)
|
||||
number_input.ui_interact(user)
|
||||
number_input.wait()
|
||||
if (number_input)
|
||||
@@ -64,14 +64,17 @@
|
||||
var/timeout
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
|
||||
/datum/tgui_input_number/New(mob/user, message, title, default, max_value, min_value, timeout, round_value)
|
||||
/datum/tgui_input_number/New(mob/user, message, title, default, max_value, min_value, timeout, round_value, ui_state)
|
||||
src.default = default
|
||||
src.max_value = max_value
|
||||
src.message = message
|
||||
src.min_value = min_value
|
||||
src.title = title
|
||||
src.round_value = round_value
|
||||
src.state = ui_state
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
@@ -87,8 +90,9 @@
|
||||
if(default > max_value)
|
||||
CRASH("Default value is greater than max value.")
|
||||
|
||||
/datum/tgui_input_number/Destroy(force, ...)
|
||||
/datum/tgui_input_number/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
@@ -110,7 +114,7 @@
|
||||
closed = TRUE
|
||||
|
||||
/datum/tgui_input_number/ui_state(mob/user)
|
||||
return GLOB.always_state
|
||||
return state
|
||||
|
||||
/datum/tgui_input_number/ui_static_data(mob/user)
|
||||
if (!user.client)
|
||||
@@ -119,10 +123,10 @@
|
||||
var/list/data = list()
|
||||
data["init_value"] = default // Default is a reserved keyword
|
||||
data["large_buttons"] = user.client.prefs.tgui_buttons_large
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["max_value"] = max_value
|
||||
data["message"] = message
|
||||
data["min_value"] = min_value
|
||||
data["swapped_buttons"] = user.client.prefs.tgui_inputs_swapped
|
||||
data["title"] = title
|
||||
data["round_value"] = round_value
|
||||
return data
|
||||
@@ -133,7 +137,7 @@
|
||||
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
return data
|
||||
|
||||
/datum/tgui_input_number/ui_act(action, list/params)
|
||||
/datum/tgui_input_number/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
sleep(3 SECONDS)
|
||||
window.initialize(
|
||||
strict_mode = TRUE,
|
||||
fancy = TRUE,
|
||||
inline_css = file("tgui/public/tgui-say.bundle.css"),
|
||||
inline_js = file("tgui/public/tgui-say.bundle.js"),
|
||||
);
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
* * message - The content of the text input, shown in the body of the TGUI window.
|
||||
* * title - The title of the text input modal, shown on the top of the TGUI window.
|
||||
* * default - The default (or current) value, shown as a placeholder.
|
||||
* * max_length - Specifies a max length for input. MAX_MESSAGE_LEN is default (1024)
|
||||
* * max_length - Specifies a max length for input. MAX_MESSAGE_LEN is default (4096)
|
||||
* * multiline - Bool that determines if the input box is much larger. Good for large messages, laws, etc.
|
||||
* * encode - Toggling this determines if input is filtered via html_encode. Setting this to FALSE gives raw input.
|
||||
* * timeout - The timeout of the textbox, after which the modal will close and qdel itself. Set to zero for no timeout.
|
||||
*/
|
||||
/proc/tgui_input_text(mob/user, message = "", title = "Text Input", default, max_length = MAX_MESSAGE_LEN, multiline = FALSE, encode = TRUE, timeout = 0)
|
||||
/proc/tgui_input_text(mob/user, message = "", title = "Text Input", default, max_length = MAX_MESSAGE_LEN, multiline = FALSE, encode = TRUE, timeout = 0, ui_state = GLOB.always_state)
|
||||
if (!user)
|
||||
user = usr
|
||||
if (!istype(user))
|
||||
@@ -39,7 +39,7 @@
|
||||
return input(user, message, title, default) as message|null
|
||||
else
|
||||
return input(user, message, title, default) as text|null
|
||||
var/datum/tgui_input_text/text_input = new(user, message, title, default, max_length, multiline, encode, timeout)
|
||||
var/datum/tgui_input_text/text_input = new(user, message, title, default, max_length, multiline, encode, timeout, ui_state)
|
||||
text_input.ui_interact(user)
|
||||
text_input.wait()
|
||||
if (text_input)
|
||||
@@ -73,21 +73,25 @@
|
||||
var/timeout
|
||||
/// The title of the TGUI window
|
||||
var/title
|
||||
/// The TGUI UI state that will be returned in ui_state(). Default: always_state
|
||||
var/datum/ui_state/state
|
||||
|
||||
/datum/tgui_input_text/New(mob/user, message, title, default, max_length, multiline, encode, timeout)
|
||||
/datum/tgui_input_text/New(mob/user, message, title, default, max_length, multiline, encode, timeout, ui_state)
|
||||
src.default = default
|
||||
src.encode = encode
|
||||
src.max_length = max_length
|
||||
src.message = message
|
||||
src.multiline = multiline
|
||||
src.title = title
|
||||
src.state = ui_state
|
||||
if (timeout)
|
||||
src.timeout = timeout
|
||||
start_time = world.time
|
||||
QDEL_IN(src, timeout)
|
||||
|
||||
/datum/tgui_input_text/Destroy(force, ...)
|
||||
/datum/tgui_input_text/Destroy(force)
|
||||
SStgui.close_uis(src)
|
||||
state = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
@@ -131,7 +135,7 @@
|
||||
data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS))
|
||||
return data
|
||||
|
||||
/datum/tgui_input_text/ui_act(action, list/params)
|
||||
/datum/tgui_input_text/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if (.)
|
||||
return
|
||||
|
||||
@@ -32,3 +32,11 @@
|
||||
tgui_panel.initialize(force = TRUE)
|
||||
// Force show the panel to see if there are any errors
|
||||
winset(src, "output_selector.legacy_output_selector", "left=output_browser")
|
||||
|
||||
/client/verb/refresh_tgui()
|
||||
set name = "Refresh TGUI"
|
||||
set category = "OOC.Debug"
|
||||
|
||||
for(var/window_id in tgui_windows)
|
||||
var/datum/tgui_window/window = tgui_windows[window_id]
|
||||
window.reinitialize()
|
||||
|
||||
Reference in New Issue
Block a user