Fix ByondUI small map preview (#90277)

## About The Pull Request
This PR should fix the problem of small previews in TGUI once and for
all (I hope).

What was causing it? Because TGUI takes a long time to open, that's why
previews were generated broken (small).

On Byond 515 this problem was not so noticeable as the interfaces opened
faster, but with the release of 516 it became much worse.
Previews were generated inside a window that was not yet open, so the
scale was broken, sometimes the window would open before the preview was
done and sent, usually with small interfaces, or when reopening.

I'm not very good at working with signals, and to tell the truth this is
my second experience with them, so I hope I did it right.

## Why It's Good For The Game
No more small map previews

<details> <summary> Video </summary>


https://github.com/user-attachments/assets/834f3820-cc6a-4f65-90e5-d6bb2a118bcf

</details>

## Changelog

🆑
fix: Fixed small character preview, color matrix preview, mech preview,
and other previews with uses ByondUI map
/🆑

---------

Co-authored-by: Gaxeer <44334376+Gaxeer@users.noreply.github.com>
This commit is contained in:
Aylong
2025-03-28 06:26:48 +02:00
committed by GitHub
parent 617b6762a8
commit 2df73da53e
21 changed files with 111 additions and 79 deletions

View File

@@ -0,0 +1,2 @@
/// Window is fully visible and we can make fragile calls
#define COMSIG_TGUI_WINDOW_VISIBLE "tgui_window_visible"

View File

@@ -19,17 +19,6 @@
/atom/movable/screen/proc/set_position(x, y, px = 0, py = 0)
if(assigned_map)
screen_loc = "[assigned_map]:[x]:[px],[y]:[py]"
ASYNC
// HACK: This fixes the character creator in 516 being small and relying on other byondui things (like cameras) to open in order to update and refresh.
// This also will fix the camera console screen being offset, Gateway, and admin pod panel.
// Adding 100 then setting it back seemed to do the trick!
// Why the fuck does this work? This is some byond bug and I honestly have no fucking clue why this works.
// I don't think plane master will be affected, I hope.
// We're stuck in the belly of this awful machine.
sleep(0.2 SECONDS) // If it's too fast, it has a chance to fail? Idk. This seems like a good number.
screen_loc = "[assigned_map]:[x+100]:[px],[y+100]:[py]"
sleep(0.2 SECONDS)
screen_loc = "[assigned_map]:[x]:[px],[y]:[py]"
else
screen_loc = "[x]:[px],[y]:[py]"

View File

@@ -28,8 +28,30 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view)
assigned_map = map_key
set_position(1, 1)
/atom/movable/screen/map_view/proc/display_to(mob/show_to)
show_to.client.register_map_obj(src)
/**
* Generates and displays the map view to a client
* Make sure you at least try to pass tgui_window if map view needed on UI,
* so it will wait a signal from TGUI, which tells windows is fully visible.
*
* If you use map view not in TGUI, just call it as usualy.
* If UI needs planes, call display_to_client.
*
* * show_to - Mob which needs map view
* * window - Optional. TGUI window which needs map view
*/
/atom/movable/screen/map_view/proc/display_to(mob/show_to, datum/tgui_window/window)
if(window && !window.visible)
RegisterSignal(window, COMSIG_TGUI_WINDOW_VISIBLE, PROC_REF(display_on_ui_visible))
else
display_to_client(show_to.client)
/atom/movable/screen/map_view/proc/display_on_ui_visible(datum/tgui_window/window, client/show_to)
SIGNAL_HANDLER
display_to_client(show_to)
UnregisterSignal(window, COMSIG_TGUI_WINDOW_VISIBLE)
/atom/movable/screen/map_view/proc/display_to_client(client/show_to)
show_to.register_map_obj(src)
// We need to add planesmasters to the popup, otherwise
// blending fucks up massively. Any planesmaster on the main screen does
// NOT apply to map popups. If there's ever a way to make planesmasters
@@ -37,7 +59,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view)
// We lazy load this because there's no point creating all these if none's gonna see em
// Store this info in a client -> hud pattern, so ghosts closing the window nukes the right group
var/datum/weakref/client_ref = WEAKREF(show_to.client)
var/datum/weakref/client_ref = WEAKREF(show_to)
var/datum/weakref/hud_ref = viewers_to_huds[client_ref]
var/datum/hud/our_hud = hud_ref?.resolve()
@@ -46,8 +68,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view)
// Generate a new plane group for this case
var/datum/plane_master_group/popup/pop_planes = new(PLANE_GROUP_POPUP_WINDOW(src), assigned_map)
viewers_to_huds[client_ref] = WEAKREF(show_to.hud_used)
pop_planes.attach_to(show_to.hud_used)
viewers_to_huds[client_ref] = WEAKREF(show_to.mob.hud_used)
pop_planes.attach_to(show_to.mob.hud_used)
return pop_planes

View File

@@ -15,9 +15,7 @@
var/list/concurrent_users = list()
// Stuff needed to render the map
var/atom/movable/screen/map_view/cam_screen
/// All the plane masters that need to be applied.
var/atom/movable/screen/background/cam_background
var/atom/movable/screen/map_view/camera/cam_screen
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_SIGHT
@@ -34,13 +32,9 @@
// Initialize map objects
cam_screen = new
cam_screen.generate_view(map_name)
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
/obj/machinery/computer/security/Destroy()
QDEL_NULL(cam_screen)
QDEL_NULL(cam_background)
return ..()
/obj/machinery/computer/security/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
@@ -69,12 +63,11 @@
if(length(concurrent_users) == 1 && is_living)
playsound(src, 'sound/machines/terminal/terminal_on.ogg', 25, FALSE)
use_energy(active_power_usage)
// Register map objects
cam_screen.display_to(user)
user.client.register_map_obj(cam_background)
// Open UI
ui = new(user, src, "CameraConsole", name)
ui.open()
// Register map objects
cam_screen.display_to(user, ui.window)
/obj/machinery/computer/security/ui_status(mob/user, datum/ui_state/state)
. = ..()
@@ -119,7 +112,7 @@
/obj/machinery/computer/security/proc/update_active_camera_screen()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
cam_screen.show_camera_static()
return
var/list/visible_turfs = list()
@@ -147,9 +140,7 @@
var/size_x = bbox[3] - bbox[1] + 1
var/size_y = bbox[4] - bbox[2] + 1
cam_screen.vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
cam_screen.show_camera(visible_turfs, size_x, size_y)
/obj/machinery/computer/security/ui_close(mob/user)
. = ..()
@@ -165,13 +156,35 @@
last_camera_turf = null
playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
/obj/machinery/computer/security/proc/show_camera_static()
cam_screen.vis_contents.Cut()
/atom/movable/screen/map_view/camera
/// All the plane masters that need to be applied.
var/atom/movable/screen/background/cam_background
/atom/movable/screen/map_view/camera/Destroy()
QDEL_NULL(cam_background)
return ..()
/atom/movable/screen/map_view/camera/generate_view(map_key)
. = ..()
cam_background = new
cam_background.del_on_map_removal = FALSE
cam_background.assigned_map = assigned_map
/atom/movable/screen/map_view/camera/display_to_client(client/show_to)
show_to.register_map_obj(cam_background)
. = ..()
/atom/movable/screen/map_view/camera/proc/show_camera(list/visible_turfs, size_x, size_y)
vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
/atom/movable/screen/map_view/camera/proc/show_camera_static()
vis_contents.Cut()
cam_background.icon_state = "scanline2"
cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE)
// SECURITY MONITORS
/obj/machinery/computer/security/wooden_tv
name = "security camera monitor"
desc = "An old TV hooked into the station's camera network."

View File

@@ -32,7 +32,6 @@
return
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
character_preview_view = create_character_preview_view(user)
ui = new(user, src, "MedicalRecords")
ui.set_autoupdate(FALSE)
ui.open()

View File

@@ -100,30 +100,31 @@
if(!target)
return FALSE
update_preview(user, params["assigned_view"], target)
update_preview(user, params["assigned_view"], target, ui.window)
return TRUE
return FALSE
/// Creates a character preview view for the UI.
/obj/machinery/computer/records/proc/create_character_preview_view(mob/user)
/obj/machinery/computer/records/proc/create_character_preview_view(mob/user, datum/tgui_window/window)
var/assigned_view = USER_PREVIEW_ASSIGNED_VIEW(user.ckey)
if(user.client?.screen_maps[assigned_view])
return
var/atom/movable/screen/map_view/char_preview/new_view = new(null, src)
new_view.generate_view(assigned_view)
new_view.display_to(user)
new_view.display_to(user, window)
return new_view
/// Takes a record and updates the character preview view to match it.
/obj/machinery/computer/records/proc/update_preview(mob/user, assigned_view, datum/record/crew/target)
/obj/machinery/computer/records/proc/update_preview(mob/user, assigned_view, datum/record/crew/target, datum/tgui_window/window)
var/mutable_appearance/preview = new(target.character_appearance)
preview.underlays += mutable_appearance('icons/effects/effects.dmi', "static_base", alpha = 20)
preview.add_overlay(mutable_appearance(generate_icon_alpha_mask('icons/effects/effects.dmi', "scanline"), alpha = 20))
var/atom/movable/screen/map_view/char_preview/old_view = user.client?.screen_maps[assigned_view]?[1]
if(!old_view)
character_preview_view = create_character_preview_view(user, window)
return
old_view.appearance = preview.appearance

View File

@@ -78,7 +78,6 @@
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
character_preview_view = create_character_preview_view(user)
ui = new(user, src, "SecurityRecords")
ui.set_autoupdate(FALSE)
ui.open()

View File

@@ -25,7 +25,6 @@
proxy_view.appearance = view
proxy_view.color = current_color
proxy_view.display_to(owner.mob)
/datum/color_matrix_editor/Destroy(force)
QDEL_NULL(proxy_view)
@@ -51,6 +50,7 @@
if(!ui)
ui = new(user, src, "ColorMatrixEditor")
ui.open()
proxy_view.display_to(owner.mob, ui.window)
/datum/color_matrix_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()

View File

@@ -83,8 +83,7 @@
// Generate movement detector (to update the view on MMI movement)
update_view_tracker = new(brain_ref, CALLBACK(src, PROC_REF(update_mmi_view)))
// Shows the view to the user foremost
mmi_view.display_to(user)
// Register map objects
user.client.register_map_obj(mmi_view_background)
update_mmi_view()
// Makes the MMI relay heard messages
@@ -97,6 +96,8 @@
if(!ui)
ui = new(user, src, "LingMMITalk")
ui.open()
// Open map view
mmi_view.display_to(user, ui.window)
/datum/action/changeling/mmi_talk/ui_close(mob/user)
var/obj/item/mmi/mmi = brain_ref.loc

View File

@@ -341,8 +341,8 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
. = ..()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
G.portal_visuals.display_to(user)
ui = new(user, src, "Gateway", name)
G.portal_visuals.display_to(user, ui.window)
ui.open()
/obj/machinery/computer/gateway_control/ui_data(mob/user)
@@ -418,7 +418,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
QDEL_NULL(cam_background)
return ..()
/atom/movable/screen/map_view/gateway_port/display_to(mob/show_to)
/atom/movable/screen/map_view/gateway_port/display_on_ui_visible(mob/show_to)
. = ..()
show_to.client.register_map_obj(cam_background)

View File

@@ -63,8 +63,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
var/obj/structure/closet/supplypod/centcompod/temp_pod //The temporary pod that is modified by this datum, then cloned. The buildObject() clone of this pod is what is launched
// Stuff needed to render the map
var/map_name
var/atom/movable/screen/map_view/cam_screen
var/atom/movable/screen/background/cam_background
var/atom/movable/screen/map_view/camera/cam_screen
var/tabIndex = 1
var/renderLighting = FALSE
var/static/list/pod_style_info
@@ -109,17 +108,13 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
cam_screen = new
cam_screen.generate_view(map_name)
var/datum/plane_master_group/planes = cam_screen.display_to(holder.mob)
// display_to doesn't send the planes to the client, so we have to do it via display_to_client
var/datum/plane_master_group/planes = cam_screen.display_to_client(holder)
if(!renderLighting)
for(var/atom/movable/screen/plane_master/instance as anything in holder.mob.hud_used.get_true_plane_masters(LIGHTING_PLANE, planes.key))
instance.set_alpha(100)
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = TRUE
refreshView()
holder.register_map_obj(cam_background)
/datum/centcom_podlauncher/ui_state(mob/user)
if (SSticker.current_state >= GAME_STATE_FINISHED)
@@ -525,7 +520,6 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
. = TRUE
if("refreshView")
initMap()
refreshView()
. = TRUE
if("renderLighting")
renderLighting = !renderLighting
@@ -553,7 +547,6 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
/datum/centcom_podlauncher/ui_close(mob/user) //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables.
QDEL_NULL(temp_pod)
QDEL_NULL(cam_screen)
QDEL_NULL(cam_background)
qdel(src)
/datum/centcom_podlauncher/proc/setupViewPod()
@@ -575,9 +568,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
var/size_x = bbox[3] - bbox[1] + 1
var/size_y = bbox[4] - bbox[2] + 1
cam_screen.vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
cam_screen.show_camera(visible_turfs, size_x, size_y)
/datum/centcom_podlauncher/proc/updateCursor(forceClear = FALSE) //Update the mouse of the user
if (!holder) //Can't update the mouse icon if the client doesnt exist!

View File

@@ -138,10 +138,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
character_preview_view = create_character_preview_view(user)
ui = new(user, src, "PreferencesMenu")
ui.set_autoupdate(FALSE)
ui.open()
character_preview_view.display_to(user, ui.window)
/datum/preferences/ui_state(mob/user)
return GLOB.always_state
@@ -287,7 +287,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
character_preview_view = new(null, src)
character_preview_view.generate_view("character_preview_[REF(character_preview_view)]")
character_preview_view.update_body()
character_preview_view.display_to(user)
return character_preview_view

View File

@@ -332,7 +332,6 @@
paint_kit.proxy_view.appearance = paint_kit.editing_mod.appearance
paint_kit.proxy_view.color = null
paint_kit.proxy_view.display_to(user)
paint_kit.ui_interact(user)
return ITEM_INTERACT_SUCCESS
else // Left click

View File

@@ -30,6 +30,7 @@
if(!ui)
ui = new(user, src, "MODpaint", name)
ui.open()
proxy_view.display_to(user, ui.window)
/obj/item/mod/paint/ui_host()
return editing_mod

View File

@@ -28,9 +28,7 @@
var/turf/last_camera_turf
// Stuff needed to render the map
var/atom/movable/screen/map_view/cam_screen
/// All the plane masters that need to be applied.
var/atom/movable/screen/background/cam_background
var/atom/movable/screen/map_view/camera/cam_screen
///Internal tracker used to find a specific person and keep them on cameras.
var/datum/trackable/internal_tracker
@@ -75,13 +73,9 @@
// Initialize map objects
cam_screen = new
cam_screen.generate_view(map_name)
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
/datum/computer_file/program/secureye/Destroy()
QDEL_NULL(cam_screen)
QDEL_NULL(cam_background)
QDEL_NULL(internal_tracker)
last_camera_turf = null
return ..()
@@ -102,8 +96,7 @@
if(is_living)
concurrent_users += user_ref
// Register map objects
cam_screen.display_to(user)
user.client.register_map_obj(cam_background)
cam_screen.display_to(user, ui.window)
/datum/computer_file/program/secureye/ui_status(mob/user, datum/ui_state/state)
. = ..()
@@ -197,7 +190,7 @@
var/obj/machinery/camera/active_camera = camera_ref?.resolve()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
cam_screen.show_camera_static()
return
var/list/visible_turfs = list()
@@ -225,13 +218,6 @@
var/size_x = bbox[3] - bbox[1] + 1
var/size_y = bbox[4] - bbox[2] + 1
cam_screen.vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, size_x, size_y)
/datum/computer_file/program/secureye/proc/show_camera_static()
cam_screen.vis_contents.Cut()
cam_background.icon_state = "scanline2"
cam_background.fill_rect(1, 1, DEFAULT_MAP_SIZE, DEFAULT_MAP_SIZE)
cam_screen.show_camera(visible_turfs, size_x, size_y)
#undef DEFAULT_MAP_SIZE

View File

@@ -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
@@ -238,6 +239,7 @@
log_tgui(client,
context = "[id]/close (suspending)",
window = src)
visible = FALSE
status = TGUI_WINDOW_READY
send_message("suspend")
return
@@ -245,6 +247,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
@@ -379,6 +382,9 @@
switch(type)
if("ping")
send_message("ping/reply", payload)
if("visible")
visible = TRUE
SEND_SIGNAL(src, COMSIG_TGUI_WINDOW_VISIBLE, client)
if("suspend")
close(can_be_suspended = TRUE)
if("close")

View File

@@ -7,7 +7,7 @@
if(!ui)
ui = new(user, src, "Mecha", name)
ui.open()
ui_view.display_to(user)
ui_view.display_to(user, ui.window)
/obj/vehicle/sealed/mecha/ui_status(mob/user, datum/ui_state/state)
var/common_status = min(

View File

@@ -373,6 +373,7 @@
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\signals_swab.dm"
#include "code\__DEFINES\dcs\signals\signals_techweb.dm"
#include "code\__DEFINES\dcs\signals\signals_tgui.dm"
#include "code\__DEFINES\dcs\signals\signals_tools.dm"
#include "code\__DEFINES\dcs\signals\signals_traitor.dm"
#include "code\__DEFINES\dcs\signals\signals_transform.dm"

View File

@@ -217,6 +217,7 @@ export const backendMiddleware = (store) => {
Byond.winset(Byond.windowId, {
'is-visible': true,
});
Byond.sendMessage('visible');
perf.mark('resume/finish');
if (process.env.NODE_ENV !== 'production') {
logger.log(

View File

@@ -96,6 +96,19 @@ const CrewTab = (props: { record: MedicalRecord }) => {
if (selectedRecord?.crew_ref === crew_ref) {
setSelectedRecord(undefined);
} else {
// GOD, I REALLY HATE IT!
// THIS FUCKING HACK NEEDED CAUSE "WINSET MAP"
// MAKING UI DISAPPEAR, AND WE NEED RE-RENDER SHIT
// AFTER BYOND DONE MAKING THEIR SHIT
// Anyway... that's better than hack before
if (selectedRecord === undefined) {
setTimeout(() => {
act('view_record', {
assigned_view: assigned_view,
crew_ref: crew_ref,
});
});
}
setSelectedRecord(record);
act('view_record', { assigned_view: assigned_view, crew_ref: crew_ref });
}

View File

@@ -97,6 +97,15 @@ const CrewTab = (props: { record: SecurityRecord }) => {
if (selectedRecord?.crew_ref === crew_ref) {
setSelectedRecord(undefined);
} else {
// See MedicalRecords/RecordTabs.tsx for explanation
if (selectedRecord === undefined) {
setTimeout(() => {
act('view_record', {
assigned_view: assigned_view,
crew_ref: crew_ref,
});
});
}
setSelectedRecord(record);
act('view_record', { assigned_view: assigned_view, crew_ref: crew_ref });
}