Screenshots and downloads are now only retrieved once from the team server when the user selects them in the client for preview, which leads to faster start-up times and less blocking UI.

This commit is contained in:
Jakob Friedl
2025-10-14 22:04:04 +02:00
parent df04eafc13
commit 1e95b67603
8 changed files with 79 additions and 55 deletions

View File

@@ -70,9 +70,9 @@ proc sendRemoveLoot*(connection: WsConnection, lootId: string) =
)
connection.ws.sendEvent(event, connection.sessionKey)
proc sendDownloadLoot*(connection: WsConnection, lootId: string) =
proc sendGetLoot*(connection: WsConnection, lootId: string) =
let event = Event(
eventType: CLIENT_LOOT_SYNC,
eventType: CLIENT_LOOT_GET,
timestamp: now().toTime().toUnix(),
data: %*{
"lootId": lootId

View File

@@ -157,18 +157,20 @@ proc main(ip: string = "localhost", port: int = 37573) =
of DOWNLOAD:
lootDownloads.items.add(lootItem)
of SCREENSHOT:
lootScreenshots.addItem(lootItem)
lootScreenshots.items.add(lootItem)
else: discard
of CLIENT_LOOT_SYNC:
let path = event.data["path"].getStr()
let file = decode(event.data["loot"].getStr())
try:
# TODO: Using native file dialogs to have the client select the output file path (does not work in WSL)
# let outFilePath = callDialogFileSave("Save Payload")
writeFile(path & "_download", file)
except IOError:
discard
of CLIENT_LOOT_DATA:
let
lootItem = event.data["loot"].to(LootItem)
data = decode(event.data["data"].getStr())
case lootItem.itemType:
of DOWNLOAD:
lootDownloads.contents[lootItem.lootId] = data
of SCREENSHOT:
lootScreenshots.addTexture(lootItem.lootId, data)
else: discard
else: discard

View File

@@ -224,9 +224,7 @@ proc draw*(component: ConsoleComponent, connection: WsConnection) =
Problems I encountered with other approaches (Multi-line Text Input, TextEditor, ...):
- https://github.com/ocornut/imgui/issues/383#issuecomment-2080346129
- https://github.com/ocornut/imgui/issues/950
Huge thanks to @dinau for implementing ImGuiTextSelect into imguin very rapidly after I requested it.
- https://github.com/ocornut/imgui/issues/950
]#
let consolePadding: float = 10.0f
let footerHeight = (consolePadding * 2) + (igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing()) * 0.75f

View File

@@ -1,4 +1,4 @@
import strformat, strutils, times, os
import strformat, strutils, times, os, tables
import imguin/[cimgui, glfw_opengl, simple]
import ../../utils/[appImGui, colors]
import ../../../common/[types, utils]
@@ -8,6 +8,7 @@ type
DownloadsComponent* = ref object of RootObj
title: string
items*: seq[LootItem]
contents*: Table[string, string]
selectedIndex: int
@@ -15,6 +16,7 @@ proc LootDownloads*(title: string): DownloadsComponent =
result = new DownloadsComponent
result.title = title
result.items = @[]
result.contents = initTable[string, string]()
result.selectedIndex = -1
proc draw*(component: DownloadsComponent, showComponent: ptr bool, connection: WsConnection) =
@@ -88,8 +90,14 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool, connection: W
let item = component.items[component.selectedIndex]
if igMenuItem("Download", nil, false, true):
# Task team server to download file
connection.sendDownloadLoot(item.lootId)
# Download file
try:
# TODO: Use native dialogs to select the download location
let path = item.path & ".download"
let data = component.contents[item.lootId]
writeFile(path, data)
except IOError:
discard
igCloseCurrentPopup()
if igMenuItem("Remove", nil, false, true):
@@ -111,15 +119,20 @@ proc draw*(component: DownloadsComponent, showComponent: ptr bool, connection: W
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len:
let item = component.items[component.selectedIndex]
igText(fmt("[{item.host}] "))
igSameLine(0.0f, 0.0f)
igText(item.path.extractFilename().replace("C_", "C:/").replace("_", "/"))
igDummy(vec2(0.0f, 5.0f))
igSeparator()
igDummy(vec2(0.0f, 5.0f))
if not component.contents.hasKey(item.lootId):
connection.sendGetLoot(item.lootId)
component.contents[item.lootId] = "" # Ensure that the sendGetLoot() function is sent only once by setting a value for the table key
igTextUnformatted(item.data, nil)
else:
igText(fmt("[{item.host}] "))
igSameLine(0.0f, 0.0f)
igText(item.path.extractFilename().replace("C_", "C:/").replace("_", "/"))
igDummy(vec2(0.0f, 5.0f))
igSeparator()
igDummy(vec2(0.0f, 5.0f))
igTextUnformatted(component.contents[item.lootId], nil)
else:
igText("Select item to preview contents")

View File

@@ -7,12 +7,13 @@ import ../../core/websocket
type
ScreenshotTexture* = ref object
textureId*: GLuint
data*: string
width: int
height: int
ScreenshotsComponent* = ref object of RootObj
title: string
items: seq[LootItem]
items*: seq[LootItem]
selectedIndex: int
textures: Table[string, ScreenshotTexture]
@@ -23,13 +24,12 @@ proc LootScreenshots*(title: string): ScreenshotsComponent =
result.selectedIndex = -1
result.textures = initTable[string, ScreenshotTexture]()
proc addItem*(component: ScreenshotsComponent, screenshot: LootItem) =
component.items.add(screenshot)
proc addTexture*(component: ScreenshotsComponent, lootId: string, data: string) =
var textureId: GLuint
let (width, height) = loadTextureFromBytes(string.toBytes(screenshot.data), textureId)
component.textures[screenshot.path] = ScreenshotTexture(
let (width, height) = loadTextureFromBytes(string.toBytes(data), textureId)
component.textures[lootId] = ScreenshotTexture(
textureId: textureId,
data: data,
width: width,
height: height
)
@@ -98,11 +98,18 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool, connection:
# Handle right-click context menu
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len and igBeginPopupContextWindow("Downloads", ImGui_PopupFlags_MouseButtonRight.int32):
let item = component.items[component.selectedIndex]
if igMenuItem("Download", nil, false, true):
# Task team server to download file
connection.sendDownloadLoot(item.lootId)
# Download screenshot
try:
# TODO: Use native dialogs to select the download location
let path = item.path & "_download.jpeg"
let data = component.textures[item.lootId].data
writeFile(path, data)
except IOError:
discard
igCloseCurrentPopup()
if igMenuItem("Remove", nil, false, true):
@@ -122,10 +129,20 @@ proc draw*(component: ScreenshotsComponent, showComponent: ptr bool, connection:
if igBeginChild_Str("##Preview", vec2(0.0f, 0.0f), ImGui_ChildFlags_Borders.int32, ImGui_WindowFlags_None.int32):
if component.selectedIndex >= 0 and component.selectedIndex < component.items.len:
let item = component.items[component.selectedIndex]
let texture = component.textures[item.path]
igImage(ImTextureRef(internal_TexData: nil, internal_TexID: texture.textureId), vec2(texture.width, texture.height), vec2(0, 0), vec2(1, 1))
let item = component.items[component.selectedIndex]
# Check if the texture for the loot item has already been loaded from the team server
# If the texture doesn't exist yet, send a request to the team server to retrieve and render it
if not component.textures.hasKey(item.lootId):
connection.sendGetLoot(item.lootId)
component.textures[item.lootId] = nil # Ensure that the sendGetLoot() function is sent only once by setting a value for the table key
# Display the image preview
else:
let texture = component.textures[item.lootId]
if not texture.isNil():
igImage(ImTextureRef(internal_TexData: nil, internal_TexID: texture.textureId), vec2(texture.width, texture.height), vec2(0, 0), vec2(1, 1))
else:
igText("Select item for preview.")