From c1912b4a26423f4ab19de79fd80c88f2cad8ec4d Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Sun, 25 Jan 2026 23:16:39 +0800 Subject: [PATCH 01/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=BF=A1=E6=81=AF=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=85=A8?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/PS.GlobalConfig.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Boilerplate_!Base/src/PS.GlobalConfig.lua b/Boilerplate_!Base/src/PS.GlobalConfig.lua index 3d3671983..cb1f73d03 100644 --- a/Boilerplate_!Base/src/PS.GlobalConfig.lua +++ b/Boilerplate_!Base/src/PS.GlobalConfig.lua @@ -205,25 +205,25 @@ function PS.OnPanelActive(wnd) nY = nY + 40 local uiMemory = ui:Append('Text', { - x = nX, y = nY, w = 150, + x = nX, y = nY, w = W - nX - nPaddingX, alpha = 150, font = 162, }) nY = nY + 25 local uiSize = ui:Append('Text', { - x = nX, y = nY, w = 150, + x = nX, y = nY, w = W - nX - nPaddingX, alpha = 150, font = 162, }) nY = nY + 25 local uiUIScale = ui:Append('Text', { - x = nX, y = nY, w = 150, + x = nX, y = nY, w = W - nX - nPaddingX, alpha = 150, font = 162, }) nY = nY + 25 local uiFontScale = ui:Append('Text', { - x = nX, y = nY, w = 150, + x = nX, y = nY, w = W - nX - nPaddingX, alpha = 150, font = 162, }) nY = nY + 25 From 67940244cdd3d1531914f5cf147265e208b9635c Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 10:27:37 +0800 Subject: [PATCH 02/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E8=BF=87=E6=BB=A4=E6=8A=A5=E9=94=99=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MY_Chat/src/MY_ChatBlock.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MY_Chat/src/MY_ChatBlock.lua b/MY_Chat/src/MY_ChatBlock.lua index ff27c6237..f0484c72f 100644 --- a/MY_Chat/src/MY_ChatBlock.lua +++ b/MY_Chat/src/MY_ChatBlock.lua @@ -126,11 +126,9 @@ function D.OnTalkFilter(nChannel, t, dwTalkerID, szName, bEcho, bOnlyShowBallon, if #O.tBlockHistory[uuid].aRecent >= 10 then table.remove(O.tBlockHistory[uuid].aRecent, 1) end - local szType, r, g, b, nFont = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_FONT[nChannel] - if szType then - nFont = GetMsgFont(szType) - r, g, b = GetMsgFontColor(nFont) - end + local szMsgType = TALK_CHANNEL_MSG_TYPE[nChannel] + local nFont = szMsgType and GetMsgFont(szMsgType) + local r, g, b = GetMsgFontColor(nFont, true) table.insert( O.tBlockHistory[uuid].aRecent, X.GetChatTimeXML(GetCurrentTime(), { From d1c1909947ed3b7c67add0f6e754446739872ce1 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 10:27:49 +0800 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E8=BF=87=E6=BB=A4=E5=8E=86=E5=8F=B2=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MY_Chat/src/MY_ChatBlock.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MY_Chat/src/MY_ChatBlock.lua b/MY_Chat/src/MY_ChatBlock.lua index f0484c72f..d23c98019 100644 --- a/MY_Chat/src/MY_ChatBlock.lua +++ b/MY_Chat/src/MY_ChatBlock.lua @@ -514,8 +514,11 @@ function PS.OnPanelActive(wnd) end for _, v in ipairs(history.aRecent) do table.insert(aXml, v) + if not X.GetPureText(v):find('\n', 1, true) then + table.insert(aXml, X.CONSTANT.XML_LINE_BREAKER) + end end - X.OutputTip(X.UI(this):HoverItemRect(), table.concat(aXml, '\n'), true, ALW.BOTTOM_TOP) + X.OutputTip(X.UI(this):HoverItemRect(), table.concat(aXml, '\n'):gsub('\t', ' '), true, ALW.BOTTOM_TOP) end, function(id, text, data) HideTip() From 5fd846ce9ae27c7a041904c0a343d9a5a2ac1743 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 13:26:54 +0800 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E9=80=9A=E8=AE=AF=E8=B0=83=E8=AF=95=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/info.ini | 9 +- Boilerplate_!Base/info.ini.zh_TW | 9 +- Boilerplate_!Base/lang/Dev/zhcn.jx3dat | 28 +- Boilerplate_!Base/lang/Dev/zhtw.jx3dat | 28 +- Boilerplate_!Base/lang/PS/zhcn.jx3dat | 1 + Boilerplate_!Base/lang/PS/zhtw.jx3dat | 1 + Boilerplate_!Base/src/BgMsgCenter.lua | 7 +- .../src/Dev_BgMsgSegmentViewer.lua | 252 +++++++++++++++ Boilerplate_!Base/src/Dev_BgMsgSender.lua | 219 +++++++++++++ Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 287 ++++++++++++++++++ Boilerplate_!Base/src/PS.Welcome.lua | 111 ++++--- Boilerplate_!Base/src/lib/Event.lua | 28 +- 12 files changed, 918 insertions(+), 62 deletions(-) create mode 100644 Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua create mode 100644 Boilerplate_!Base/src/Dev_BgMsgSender.lua create mode 100644 Boilerplate_!Base/src/Dev_BgMsgViewer.lua diff --git a/Boilerplate_!Base/info.ini b/Boilerplate_!Base/info.ini index e29546388..e890d81b4 100644 --- a/Boilerplate_!Base/info.ini +++ b/Boilerplate_!Base/info.ini @@ -108,6 +108,9 @@ lua_100=src\DebugLogs.lua lua_101=src\Dev_UIManager.lua lua_102=src\Dev_UIEditor.lua lua_103=src\Dev_UIFindStation.lua -lua_104=src\PS.Welcome.lua -lua_105=src\PS.GlobalConfig.lua -lua_106=src\PS.UISample.lua +lua_104=src\Dev_BgMsgSender.lua +lua_105=src\Dev_BgMsgViewer.lua +lua_106=src\Dev_BgMsgSegmentViewer.lua +lua_107=src\PS.Welcome.lua +lua_108=src\PS.GlobalConfig.lua +lua_109=src\PS.UISample.lua diff --git a/Boilerplate_!Base/info.ini.zh_TW b/Boilerplate_!Base/info.ini.zh_TW index 81b3010eb..b118302b5 100644 --- a/Boilerplate_!Base/info.ini.zh_TW +++ b/Boilerplate_!Base/info.ini.zh_TW @@ -108,6 +108,9 @@ lua_100=src\DebugLogs.lua lua_101=src\Dev_UIManager.lua lua_102=src\Dev_UIEditor.lua lua_103=src\Dev_UIFindStation.lua -lua_104=src\PS.Welcome.lua -lua_105=src\PS.GlobalConfig.lua -lua_106=src\PS.UISample.lua +lua_104=src\Dev_BgMsgSender.lua +lua_105=src\Dev_BgMsgViewer.lua +lua_106=src\Dev_BgMsgSegmentViewer.lua +lua_107=src\PS.Welcome.lua +lua_108=src\PS.GlobalConfig.lua +lua_109=src\PS.UISample.lua diff --git a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat index 4029deef3..fe192ba0e 100644 --- a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat @@ -9,6 +9,32 @@ return { -- Dev_UIEditor.lua ['Dev_UIEditor'] = '窗口查看', + -- Dev_BgMsgViewer.lua + ['BgMsgViewer'] = '背景消息查看', + ['Recording'] = '记录中', + ['Clear'] = '清空', + ['Segment Viewer'] = '分片查看', + ['BgMsg Sender'] = '消息发送', + + -- Dev_BgMsgSender.lua + ['BgMsgSender'] = '背景消息发送', + ['Channel:'] = '频道:', + ['Select channel'] = '选择频道', + ['Target:'] = '目标:', + ['MsgID:'] = '消息ID:', + ['Data:'] = '数据:', + ['Send'] = '发送', + ['Please select a channel first.'] = '请先选择频道。', + ['Please input MsgID.'] = '请输入消息ID。', + ['Data decode failed, please check Lua syntax.'] = '数据解码失败,请检查Lua语法。', + ['Whisper channel requires target name.'] = '密聊频道需要输入目标名字。', + ['BgMsg sent: %s'] = '背景消息已发送:%s', + + -- Dev_BgMsgSegmentViewer.lua + ['BgMsgSegmentViewer'] = '背景消息分片', + ['Show incomplete only'] = '仅显示未完成', + ['Clear completed'] = '清除已完成', + -- Dev_UIManager.lua ['Dev_UIManager'] = '窗口枚举', UI_DESC = { @@ -64,7 +90,7 @@ return { ['Normal2/ST_UI' ] = 'DBM_倒计时', ['Topmost/BreatheBar' ] = 'BreatheBar', ['Topmost/LoginMotp' ] = 'LoginMotp', - ['Topmost/OTActionBar' ] = '读条显示面板', + ['Topmost/OTActionBar' ] = '运功显示面板', ['Topmost/TargetMark' ] = '官方目标头顶标记', ['Topmost1/BattleTipPanel' ] = '战场提示信息', ['Topmost1/PopupMenuPanel' ] = '游戏所有弹出菜单', diff --git a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat index 41d7d1c44..8b65cc292 100644 --- a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat @@ -9,6 +9,32 @@ return { -- Dev_UIEditor.lua ['Dev_UIEditor'] = '绐楀彛鏌ョ湅', + -- Dev_BgMsgViewer.lua + ['BgMsgViewer'] = '鑳屾櫙娑堟伅鏌ョ湅', + ['Recording'] = '瑷橀寗涓', + ['Clear'] = '娓呯┖', + ['Segment Viewer'] = '鍒嗙墖鏌ョ湅', + ['BgMsg Sender'] = '娑堟伅鐧奸', + + -- Dev_BgMsgSender.lua + ['BgMsgSender'] = '鑳屾櫙娑堟伅鐧奸', + ['Channel:'] = '闋婚亾锛', + ['Select channel'] = '閬告搰闋婚亾', + ['Target:'] = '鐩锛', + ['MsgID:'] = '娑堟伅ID锛', + ['Data:'] = '鏁告摎锛', + ['Send'] = '鐧奸', + ['Please select a channel first.'] = '璜嬪厛閬告搰闋婚亾銆', + ['Please input MsgID.'] = '璜嬭几鍏ユ秷鎭疘D銆', + ['Data decode failed, please check Lua syntax.'] = '鏁告摎瑙g⒓澶辨晽锛岃珛妾㈡煡Lua瑾炴硶銆', + ['Whisper channel requires target name.'] = '瀵嗚亰闋婚亾闇瑕佽几鍏ョ洰妯欏悕瀛椼', + ['BgMsg sent: %s'] = '鑳屾櫙娑堟伅宸茬櫦閫侊細%s', + + -- Dev_BgMsgSegmentViewer.lua + ['BgMsgSegmentViewer'] = '鑳屾櫙娑堟伅鍒嗙墖', + ['Show incomplete only'] = '鍍呴’绀烘湭瀹屾垚', + ['Clear completed'] = '娓呴櫎宸插畬鎴', + -- Dev_UIManager.lua ['Dev_UIManager'] = '绐楀彛鏋氳垑', UI_DESC = { @@ -64,7 +90,7 @@ return { ['Normal2/ST_UI' ] = 'DBM_鍊掕▓鏅', ['Topmost/BreatheBar' ] = 'BreatheBar', ['Topmost/LoginMotp' ] = 'LoginMotp', - ['Topmost/OTActionBar' ] = '璁姊濋’绀洪潰鏉', + ['Topmost/OTActionBar' ] = '閬嬪姛椤ず闈㈡澘', ['Topmost/TargetMark' ] = '瀹樻柟鐩闋爞妯欒', ['Topmost1/BattleTipPanel' ] = '鎴板牬鎻愮ず淇℃伅', ['Topmost1/PopupMenuPanel' ] = '娓告埐鎵鏈夊綀鍑鸿彍鍠', diff --git a/Boilerplate_!Base/lang/PS/zhcn.jx3dat b/Boilerplate_!Base/lang/PS/zhcn.jx3dat index 4fce541fb..81ea356d8 100644 --- a/Boilerplate_!Base/lang/PS/zhcn.jx3dat +++ b/Boilerplate_!Base/lang/PS/zhcn.jx3dat @@ -32,6 +32,7 @@ return { ['No error message found.'] = '没有找到错误日志记录。', ['Open error message folder'] = '打开错误日志记录所在文件夹', ['Open logs folder'] = '打开插件日志文件夹', + ['Open BgMsgViewer'] = '打开背景通讯调试工具', ['Report bugs'] = '问题反馈', ['Enable debug tools'] = '启用调试工具', ['Debug tools has been enabled...'] = '测试工具已激活_(:彡」∠)_', diff --git a/Boilerplate_!Base/lang/PS/zhtw.jx3dat b/Boilerplate_!Base/lang/PS/zhtw.jx3dat index 039efd43a..4064ef6e9 100644 --- a/Boilerplate_!Base/lang/PS/zhtw.jx3dat +++ b/Boilerplate_!Base/lang/PS/zhtw.jx3dat @@ -32,6 +32,7 @@ return { ['No error message found.'] = '娌掓湁鎵惧埌閷鏃ヨ獙瑷橀寗銆', ['Open error message folder'] = '鎵撻枊閷鏃ヨ獙瑷橀寗鎵鍦ㄦ枃浠跺ぞ', ['Open logs folder'] = '鎵撻枊鎻掍欢鏃ヨ獙鏂囦欢澶', + ['Open BgMsgViewer'] = '鎵撻枊鑳屾櫙閫氳▕瑾胯│宸ュ叿', ['Report bugs'] = '鍟忛鍙嶉', ['Enable debug tools'] = '鍟熺敤瑾胯│宸ュ叿', ['Debug tools has been enabled...'] = '娓│宸ュ叿宸叉縺娲籣(:褰°嶁垹)_', diff --git a/Boilerplate_!Base/src/BgMsgCenter.lua b/Boilerplate_!Base/src/BgMsgCenter.lua index 4beec1453..a833b4ab0 100644 --- a/Boilerplate_!Base/src/BgMsgCenter.lua +++ b/Boilerplate_!Base/src/BgMsgCenter.lua @@ -77,10 +77,13 @@ end) -- 测试用(调试工具) X.RegisterBgMsg(X.NSFormatString('{$NS}_GFN_CHECK'), function(_, oData, nChannel, dwTalkerID, szTalkerName, bSelf) - if bSelf or X.IsDebugging() then + if bSelf or not X.IsTable(oData) or not X.IsString(oData[1]) or not X.IsString(oData[2]) then return end - X.SendBgMsg(szTalkerName, X.NSFormatString('{$NS}_GFN_REPLY'), {oData[1], X.XpCall(X.Get(_G, oData[2]), select(3, X.Unpack(oData)))}, true) + local res = oData[2]:find('return ') + and {X.XpCall(X.DecodeLUAData(oData[2]), _G)} + or {X.XpCall(X.Get(_G, oData[2]), select(3, X.Unpack(oData)))} + X.SendBgMsg(szTalkerName, X.NSFormatString('{$NS}_GFN_REPLY'), {oData[1], res}, true) end) -- 进组查看属性 diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua new file mode 100644 index 000000000..cb390f106 --- /dev/null +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua @@ -0,0 +1,252 @@ +-------------------------------------------------------------------------------- +-- This file is part of the JX3 Plugin Project. +-- @desc : 背景通讯分片查看器 +-- @copyright: Emil Zhai +-------------------------------------------------------------------------------- +---@class (partial) Boilerplate +local X = Boilerplate +-------------------------------------------------------------------------------- +local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSegmentViewer') +-------------------------------------------------------------------------------- +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] +-------------------------------------------------------------------------------- +local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') +-------------------------------------------------------------------------------- + +local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgSegmentViewer') + +local O = { + bFilterIncomplete = false, -- 仅显示未组装的 + tSegments = {}, -- { [szMsgUUID] = { szMsgID, nChannel, dwID, szName, nSegCount, aParts = { [nSegIndex] = { szPart, nTime } } } } +} +local D = {} + +-- 获取频道名称 +function D.GetChannelName(nChannel) + local szMsgType = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_MSG_TYPE[nChannel] + return szMsgType and g_tStrings.tChannelName[szMsgType] or tostring(nChannel) +end + +-- 记录分片 (外部调用入口) +function D.RecordSegment(szMsgID, szMsgUUID, nChannel, dwID, szName, nSegCount, nSegIndex, szPart) + if not O.tSegments[szMsgUUID] then + O.tSegments[szMsgUUID] = { + szMsgID = szMsgID, + szMsgUUID = szMsgUUID, + nChannel = nChannel, + dwID = dwID, + szName = szName, + nSegCount = nSegCount, + nTime = GetCurrentTime(), + aParts = {}, + } + end + O.tSegments[szMsgUUID].aParts[nSegIndex] = { + szPart = szPart, + nTime = GetCurrentTime(), + } + D.RefreshList() +end + +-- 标记消息完成 +function D.MarkComplete(szMsgUUID) + if O.tSegments[szMsgUUID] then + O.tSegments[szMsgUUID].bComplete = true + D.RefreshList() + end +end + +-- 获取已接收分片数 +function D.GetReceivedCount(tSeg) + local nCount = 0 + for _, _ in pairs(tSeg.aParts) do + nCount = nCount + 1 + end + return nCount +end + +-- 检查是否完整 +function D.IsComplete(tSeg) + return tSeg.bComplete or D.GetReceivedCount(tSeg) >= tSeg.nSegCount +end + +-- 刷新列表 +function D.RefreshList() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local uiList = X.UI(frame):Fetch('WndListBox_Segments') + if not uiList:Raw() then + return + end + uiList:ListBox('clear') + for szMsgUUID, tSeg in pairs(O.tSegments) do + local bComplete = D.IsComplete(tSeg) + -- 过滤:仅显示未组装的 + if not O.bFilterIncomplete or not bComplete then + local szTime = X.FormatTime(tSeg.nTime, '%hh:%mm:%ss') + local szChannel = D.GetChannelName(tSeg.nChannel) + local nRecv = D.GetReceivedCount(tSeg) + local szStatus = bComplete and '[OK]' or string.format('[%d/%d]', nRecv, tSeg.nSegCount) + local szText = string.format('%s %s %s %s (%s)', szTime, szStatus, szChannel, tSeg.szMsgID, tSeg.szName) + local r, g, b = 255, 255, 255 + if bComplete then + r, g, b = 128, 255, 128 + elseif nRecv < tSeg.nSegCount then + r, g, b = 255, 255, 128 + end + uiList:ListBox('insert', { + id = szMsgUUID, + text = szText, + data = tSeg, + r = r, g = g, b = b, + }) + end + end +end + +-- 显示详情 +function D.ShowDetail(tSeg) + if not tSeg then + return + end + local aLines = {} + table.insert(aLines, '========== Segment Detail ==========') + table.insert(aLines, 'Time: ' .. X.FormatTime(tSeg.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + table.insert(aLines, 'MsgID: ' .. tostring(tSeg.szMsgID)) + table.insert(aLines, 'MsgUUID: ' .. tostring(tSeg.szMsgUUID)) + table.insert(aLines, 'Channel: ' .. D.GetChannelName(tSeg.nChannel) .. ' (' .. tostring(tSeg.nChannel) .. ')') + table.insert(aLines, 'Sender: ' .. tostring(tSeg.szName) .. ' (' .. tostring(tSeg.dwID) .. ')') + table.insert(aLines, 'SegCount: ' .. tostring(tSeg.nSegCount)) + table.insert(aLines, 'Received: ' .. tostring(D.GetReceivedCount(tSeg))) + table.insert(aLines, 'Complete: ' .. tostring(D.IsComplete(tSeg))) + table.insert(aLines, '') + table.insert(aLines, '---------- Segments ----------') + for i = 1, tSeg.nSegCount do + local part = tSeg.aParts[i] + if part then + table.insert(aLines, string.format('[%d] Received at %s', i, X.FormatTime(part.nTime, '%hh:%mm:%ss'))) + table.insert(aLines, ' ' .. tostring(part.szPart)) + else + table.insert(aLines, string.format('[%d] (Missing)', i)) + end + end + X.UI.OpenTextEditor(table.concat(aLines, '\n'), { + title = 'Segment Detail - ' .. tostring(tSeg.szMsgID), + w = 600, + h = 500, + }) +end + +-- 打开界面 +function D.Open() + if D.IsOpened() then + D.Close() + return + end + local ui = X.UI.CreateFrame(FRAME_NAME, { + w = 700, + h = 500, + text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgSegmentViewer'], + anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, + close = true, + esc = true, + resize = true, + minimize = true, + onSizeChange = function() + D.OnResize() + end, + }) + local nW, nH = ui:ContainerSize() + -- 工具栏 + local nX = 10 + ui:Append('WndCheckBox', { + name = 'WndCheckBox_FilterIncomplete', + x = nX, y = 50, w = 'auto', + text = _L['Show incomplete only'], + checked = O.bFilterIncomplete, + onCheck = function(bChecked) + O.bFilterIncomplete = bChecked + D.RefreshList() + end, + }) + nX = nX + 180 + ui:Append('WndButton', { + name = 'WndButton_Clear', + x = nX, y = 50, w = 80, h = 25, + text = _L['Clear'], + onClick = function() + O.tSegments = {} + D.RefreshList() + end, + }) + nX = nX + 90 + ui:Append('WndButton', { + name = 'WndButton_ClearComplete', + x = nX, y = 50, w = 120, h = 25, + text = _L['Clear completed'], + onClick = function() + for szMsgUUID, tSeg in pairs(O.tSegments) do + if D.IsComplete(tSeg) then + O.tSegments[szMsgUUID] = nil + end + end + D.RefreshList() + end, + }) + -- 列表 + ui:Append('WndListBox', { + name = 'WndListBox_Segments', + x = 10, y = 85, + w = nW - 20, + h = nH - 95, + }) + X.UI(ui:Raw()):Fetch('WndListBox_Segments'):ListBox('onlclick', function(id, text, data, selected) + D.ShowDetail(data) + end) + D.RefreshList() +end + +function D.OnResize() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local nW, nH = ui:ContainerSize() + ui:Fetch('WndListBox_Segments'):Size(nW - 20, nH - 95) +end + +function D.Close() + X.UI.CloseFrame(FRAME_NAME) +end + +function D.IsOpened() + return Station.Lookup('Normal/' .. FRAME_NAME) ~= nil +end + +-------------------------------------------------------------------------------- +-- 全局导出 +-------------------------------------------------------------------------------- +do +local settings = { + name = FRAME_NAME, + exports = { + { + preset = 'UIEvent', + fields = { + 'Open', + 'Close', + 'IsOpened', + 'RecordSegment', + 'MarkComplete', + }, + root = D, + }, + }, +} +_G[FRAME_NAME] = X.CreateModule(settings) +end + +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'FINISH')--[[#DEBUG END]] diff --git a/Boilerplate_!Base/src/Dev_BgMsgSender.lua b/Boilerplate_!Base/src/Dev_BgMsgSender.lua new file mode 100644 index 000000000..dbc91ea4f --- /dev/null +++ b/Boilerplate_!Base/src/Dev_BgMsgSender.lua @@ -0,0 +1,219 @@ +-------------------------------------------------------------------------------- +-- This file is part of the JX3 Plugin Project. +-- @desc : 背景通讯发送器 +-- @copyright: Emil Zhai +-------------------------------------------------------------------------------- +---@class (partial) Boilerplate +local X = Boilerplate +-------------------------------------------------------------------------------- +local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSender') +-------------------------------------------------------------------------------- +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] +-------------------------------------------------------------------------------- +local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') +-------------------------------------------------------------------------------- + +local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgSender') + +local O = { + nChannel = nil, + szTarget = '', + szMsgID = '', + szData = '', +} +local D = {} + +-- 获取频道名称 +function D.GetChannelName(nChannel) + local szMsgType = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_MSG_TYPE[nChannel] + return szMsgType and g_tStrings.tChannelName[szMsgType] or tostring(nChannel) +end + +-- 获取可用频道列表 +function D.GetChannelList() + return { + { nChannel = PLAYER_TALK_CHANNEL.WHISPER, szName = D.GetChannelName(PLAYER_TALK_CHANNEL.WHISPER) or 'Whisper' }, + { nChannel = PLAYER_TALK_CHANNEL.TEAM, szName = D.GetChannelName(PLAYER_TALK_CHANNEL.TEAM) or 'Team' }, + { nChannel = PLAYER_TALK_CHANNEL.RAID, szName = D.GetChannelName(PLAYER_TALK_CHANNEL.RAID) or 'Raid' }, + { nChannel = PLAYER_TALK_CHANNEL.BATTLE_FIELD, szName = D.GetChannelName(PLAYER_TALK_CHANNEL.BATTLE_FIELD) or 'BattleField' }, + { nChannel = PLAYER_TALK_CHANNEL.TONG, szName = D.GetChannelName(PLAYER_TALK_CHANNEL.TONG) or 'Guild' }, + } +end + +-- 发送消息 +function D.SendMessage() + local nChannel = O.nChannel + local szTarget = O.szTarget + local szMsgID = O.szMsgID + local szData = O.szData + if not nChannel then + X.OutputSystemMessage(_L['Please select a channel first.']) + return + end + if X.IsEmpty(szMsgID) then + X.OutputSystemMessage(_L['Please input MsgID.']) + return + end + local oData = nil + if not X.IsEmpty(szData) then + oData = X.DecodeLUAData(szData) + if oData == nil and szData ~= 'nil' then + X.OutputSystemMessage(_L['Data decode failed, please check Lua syntax.']) + return + end + end + -- 如果是密聊频道,使用目标名字作为频道 + local xChannel = nChannel + if nChannel == PLAYER_TALK_CHANNEL.WHISPER then + if X.IsEmpty(szTarget) then + X.OutputSystemMessage(_L['Whisper channel requires target name.']) + return + end + xChannel = szTarget + end + X.SendBgMsg(xChannel, szMsgID, oData) + X.OutputSystemMessage(_L('BgMsg sent: %s', szMsgID)) +end + +-- 打开界面 +function D.Open() + if D.IsOpened() then + D.Close() + return + end + local ui = X.UI.CreateFrame(FRAME_NAME, { + w = 500, + h = 400, + text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgSender'], + anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, + close = true, + esc = true, + resize = false, + minimize = true, + }) + local nW, nH = ui:ContainerSize() + local nY = 50 + -- 频道选择 + ui:Append('Text', { + x = 10, y = nY, w = 80, h = 25, + text = _L['Channel:'], + }) + ui:Append('WndComboBox', { + name = 'WndComboBox_Channel', + x = 90, y = nY, w = 200, h = 25, + text = O.nChannel and D.GetChannelName(O.nChannel) or _L['Select channel'], + menu = function() + local menu = {} + for _, v in ipairs(D.GetChannelList()) do + table.insert(menu, { + szOption = v.szName, + fnAction = function() + O.nChannel = v.nChannel + X.UI(Station.Lookup('Normal/' .. FRAME_NAME)):Fetch('WndComboBox_Channel'):Text(v.szName) + D.UpdateTargetVisibility() + X.UI.ClosePopupMenu() + end, + }) + end + return menu + end, + }) + nY = nY + 35 + -- 目标名字(仅密聊时启用) + ui:Append('Text', { + name = 'Text_Target', + x = 10, y = nY, w = 80, h = 25, + text = _L['Target:'], + }) + ui:Append('WndEditBox', { + name = 'WndEditBox_Target', + x = 90, y = nY, w = 200, h = 25, + text = O.szTarget, + enable = O.nChannel == PLAYER_TALK_CHANNEL.WHISPER, + onChange = function(szText) + O.szTarget = szText + end, + }) + nY = nY + 35 + -- MsgID + ui:Append('Text', { + x = 10, y = nY, w = 80, h = 25, + text = _L['MsgID:'], + }) + ui:Append('WndEditBox', { + name = 'WndEditBox_MsgID', + x = 90, y = nY, w = 390, h = 25, + text = O.szMsgID, + onChange = function(szText) + O.szMsgID = szText + end, + }) + nY = nY + 35 + -- Data + ui:Append('Text', { + x = 10, y = nY, w = 80, h = 25, + text = _L['Data:'], + }) + nY = nY + 30 + ui:Append('WndEditBox', { + name = 'WndEditBox_Data', + x = 10, y = nY, w = nW - 20, h = 180, + multiline = true, + text = O.szData, + onChange = function(szText) + O.szData = szText + end, + }) + nY = nY + 190 + -- 发送按钮 + ui:Append('WndButton', { + name = 'WndButton_Send', + x = (nW - 100) / 2, y = nY, w = 100, h = 30, + text = _L['Send'], + onClick = function() + D.SendMessage() + end, + }) +end + +function D.UpdateTargetVisibility() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local bEnable = O.nChannel == PLAYER_TALK_CHANNEL.WHISPER + ui:Fetch('Text_Target'):Alpha(bEnable and 255 or 128) + ui:Fetch('WndEditBox_Target'):Enable(bEnable):Alpha(bEnable and 255 or 128) +end + +function D.Close() + X.UI.CloseFrame(FRAME_NAME) +end + +function D.IsOpened() + return Station.Lookup('Normal/' .. FRAME_NAME) ~= nil +end + +-------------------------------------------------------------------------------- +-- 全局导出 +-------------------------------------------------------------------------------- +do +local settings = { + name = FRAME_NAME, + exports = { + { + preset = 'UIEvent', + fields = { + 'Open', + 'Close', + 'IsOpened', + }, + root = D, + }, + }, +} +_G[FRAME_NAME] = X.CreateModule(settings) +end + +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'FINISH')--[[#DEBUG END]] diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua new file mode 100644 index 000000000..8e7036dce --- /dev/null +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -0,0 +1,287 @@ +-------------------------------------------------------------------------------- +-- This file is part of the JX3 Plugin Project. +-- @desc : 背景通讯查看器 +-- @copyright: Emil Zhai +-------------------------------------------------------------------------------- +---@class (partial) Boilerplate +local X = Boilerplate +-------------------------------------------------------------------------------- +local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgViewer') +-------------------------------------------------------------------------------- +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] +-------------------------------------------------------------------------------- +local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') +-------------------------------------------------------------------------------- + +local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgViewer') +local STORAGE_FILE = {'temporary/bgmsg_viewer.jx3dat', X.PATH_TYPE.ROLE} +local MAX_HISTORY = 1000 + +local O = { + bRecording = false, + aHistory = {}, +} +local D = {} + +-- 加载持久化数据 +function D.LoadStorage() + local data = X.LoadLUAData(STORAGE_FILE) + if X.IsTable(data) then + O.bRecording = data.bRecording or false + O.aHistory = data.aHistory or {} + end +end + +-- 保存持久化数据 +function D.SaveStorage() + X.SaveLUAData(STORAGE_FILE, { + bRecording = O.bRecording, + aHistory = O.aHistory, + }) +end + +-- 获取频道名称 +function D.GetChannelName(nChannel) + local szMsgType = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_MSG_TYPE[nChannel] + return szMsgType and g_tStrings.tChannelName[szMsgType] or tostring(nChannel) +end + +-- 记录消息 (外部调用入口) +-- szDirection: 'IN' 入站, 'OUT' 出站 +function D.RecordMessage(szMsgID, nChannel, dwID, szName, bSelf, aMsg, oData, nSegCount, szDirection) + if not O.bRecording then + return + end + local szMsgUUID = aMsg and aMsg[1] and aMsg[1].u or '' + local nSegIndex = aMsg and aMsg[1] and aMsg[1].i or 0 + local szPart = aMsg and aMsg[2] or '' + local rec = { + szMsgID = szMsgID, + nChannel = nChannel, + dwID = dwID, + szName = szName, + bSelf = bSelf, + szMsgUUID = szMsgUUID, + nSegCount = nSegCount or 1, + nSegIndex = nSegIndex, + szPart = szPart, + oData = oData, + bComplete = oData ~= nil, + nTime = GetCurrentTime(), + szDirection = szDirection or 'IN', + szTarget = type(nChannel) == 'string' and nChannel or nil, + } + table.insert(O.aHistory, rec) + -- 限制最大记录数 + while #O.aHistory > MAX_HISTORY do + table.remove(O.aHistory, 1) + end + -- 更新界面 + D.RefreshList() +end + +-- 刷新列表 +function D.RefreshList() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local uiList = X.UI(frame):Fetch('WndListBox_History') + if not uiList:Raw() then + return + end + uiList:ListBox('clear') + for i, rec in ipairs(O.aHistory) do + local szTime = X.FormatTime(rec.nTime, '%hh:%mm:%ss') + local szChannel = D.GetChannelName(rec.nChannel) + local szStatus = rec.bComplete and '[OK]' or '[..]' + local szDir = rec.szDirection == 'OUT' and '[OUT]' or '[IN]' + local szText = string.format('%s %s %s %s %s (%s)', szTime, szDir, szStatus, szChannel, rec.szMsgID, rec.szName) + local r, g, b = 255, 255, 255 + if rec.szDirection == 'OUT' then + r, g, b = 128, 200, 255 + elseif rec.bSelf then + r, g, b = 128, 255, 128 + elseif not rec.bComplete then + r, g, b = 255, 255, 128 + end + uiList:ListBox('insert', { + id = i, + text = szText, + data = rec, + r = r, g = g, b = b, + }) + end +end + +-- 显示详情 +function D.ShowDetail(rec) + if not rec then + return + end + local aLines = {} + table.insert(aLines, '========== BgMsg Detail ==========') + table.insert(aLines, 'Time: ' .. X.FormatTime(rec.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + table.insert(aLines, 'Direction: ' .. tostring(rec.szDirection or 'IN')) + table.insert(aLines, 'MsgID: ' .. tostring(rec.szMsgID)) + table.insert(aLines, 'MsgUUID: ' .. tostring(rec.szMsgUUID)) + table.insert(aLines, 'Channel: ' .. D.GetChannelName(rec.nChannel) .. ' (' .. tostring(rec.nChannel) .. ')') + if rec.szTarget then + table.insert(aLines, 'Target: ' .. tostring(rec.szTarget)) + end + table.insert(aLines, 'Sender: ' .. tostring(rec.szName) .. ' (' .. tostring(rec.dwID) .. ')') + table.insert(aLines, 'IsSelf: ' .. tostring(rec.bSelf)) + table.insert(aLines, 'SegCount: ' .. tostring(rec.nSegCount)) + table.insert(aLines, 'SegIndex: ' .. tostring(rec.nSegIndex)) + table.insert(aLines, 'Complete: ' .. tostring(rec.bComplete)) + table.insert(aLines, '') + table.insert(aLines, '---------- Raw Part ----------') + table.insert(aLines, tostring(rec.szPart)) + table.insert(aLines, '') + table.insert(aLines, '---------- Decoded Data ----------') + if rec.oData ~= nil then + table.insert(aLines, X.EncodeLUAData(rec.oData, ' ')) + else + table.insert(aLines, '(Not yet decoded or decode failed)') + end + X.UI.OpenTextEditor(table.concat(aLines, '\n'), { + title = 'BgMsg Detail - ' .. tostring(rec.szMsgID), + w = 600, + h = 500, + }) +end + +-- 打开界面 +function D.Open() + if D.IsOpened() then + D.Close() + return + end + local ui = X.UI.CreateFrame(FRAME_NAME, { + w = 700, + h = 500, + text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgViewer'], + anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, + close = true, + esc = true, + resize = true, + minimize = true, + onSizeChange = function() + D.OnResize() + end, + }) + local nW, nH = ui:ContainerSize() + -- 工具栏 + local nX = 10 + ui:Append('WndCheckBox', { + name = 'WndCheckBox_Recording', + x = nX, y = 50, w = 'auto', + text = _L['Recording'], + checked = O.bRecording, + onCheck = function(bChecked) + O.bRecording = bChecked + D.SaveStorage() + end, + }) + nX = nX + 100 + ui:Append('WndButton', { + name = 'WndButton_Clear', + x = nX, y = 50, w = 80, h = 25, + text = _L['Clear'], + onClick = function() + O.aHistory = {} + D.RefreshList() + D.SaveStorage() + end, + }) + nX = nX + 90 + ui:Append('WndButton', { + name = 'WndButton_Segment', + x = nX, y = 50, w = 120, h = 25, + text = _L['Segment Viewer'], + onClick = function() + local szSegmentViewerName = X.NSFormatString('{$NS}_BgMsgSegmentViewer') + if _G[szSegmentViewerName] and _G[szSegmentViewerName].Open then + _G[szSegmentViewerName].Open() + end + end, + }) + nX = nX + 130 + ui:Append('WndButton', { + name = 'WndButton_Sender', + x = nX, y = 50, w = 120, h = 25, + text = _L['BgMsg Sender'], + onClick = function() + local szSenderName = X.NSFormatString('{$NS}_BgMsgSender') + if _G[szSenderName] and _G[szSenderName].Open then + _G[szSenderName].Open() + end + end, + }) + -- 列表 + ui:Append('WndListBox', { + name = 'WndListBox_History', + x = 10, y = 85, + w = nW - 20, + h = nH - 95, + }) + X.UI(ui:Raw()):Fetch('WndListBox_History'):ListBox('onlclick', function(id, text, data, selected) + D.ShowDetail(data) + end) + D.RefreshList() +end + +function D.OnResize() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local nW, nH = ui:ContainerSize() + ui:Fetch('WndListBox_History'):Size(nW - 20, nH - 95) +end + +function D.Close() + X.UI.CloseFrame(FRAME_NAME) +end + +function D.IsOpened() + return Station.Lookup('Normal/' .. FRAME_NAME) ~= nil +end + +function D.IsRecording() + return O.bRecording +end + +-- 初始化 +X.RegisterInit(function() + D.LoadStorage() +end) +X.RegisterFlush(function() + D.SaveStorage() +end) + +-------------------------------------------------------------------------------- +-- 全局导出 +-------------------------------------------------------------------------------- +do +local settings = { + name = FRAME_NAME, + exports = { + { + preset = 'UIEvent', + fields = { + 'Open', + 'Close', + 'IsOpened', + 'IsRecording', + 'RecordMessage', + }, + root = D, + }, + }, +} +_G[FRAME_NAME] = X.CreateModule(settings) +end + +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'FINISH')--[[#DEBUG END]] diff --git a/Boilerplate_!Base/src/PS.Welcome.lua b/Boilerplate_!Base/src/PS.Welcome.lua index c7c70bc16..be33dc6bc 100644 --- a/Boilerplate_!Base/src/PS.Welcome.lua +++ b/Boilerplate_!Base/src/PS.Welcome.lua @@ -145,56 +145,7 @@ function PS.OnPanelActive(wnd) x = x, h = 30, text = _L['Error message'], menu = function() - local menu = { - { - szOption = _L['Show error message'], - tip = { - render = _L['Show error message, please commit it while report bugs'], - position = X.UI.TIP_POSITION.BOTTOM_TOP, - }, - fnAction = function() - local szErrmsg = X.GetAddonErrorMessage() - local nErrmsgLen, nMaxLen = #szErrmsg, 1024 - if nErrmsgLen == 0 then - X.Alert(_L['No error message found.']) - return - end - if nErrmsgLen > 300 then - szErrmsg = szErrmsg:sub(0, nMaxLen) - .. '\n========================================' - .. '\n' .. '... ' .. (nErrmsgLen - nMaxLen) .. ' char(s) omitted.' - .. '\n========================================' - .. '\n# Full error logs:' - .. '\n> ' .. X.GetAbsolutePath(X.GetAddonErrorMessageFilePath()) - .. '\n========================================' - end - X.UI.OpenTextEditor(szErrmsg, { w = 800, h = 600, title = _L['Error message'] }) - X.UI.ClosePopupMenu() - end, - }, - { - szOption = _L['Open error message folder'], - fnAction = function() - X.OpenFolder(X.GetAbsolutePath(X.GetAddonErrorMessageFilePath())) - X.UI.ClosePopupMenu() - end, - }, - { - szOption = _L['Open logs folder'], - fnAction = function() - X.OpenFolder(X.GetAbsolutePath(X.FormatPath({'logs/', X.PATH_TYPE.ROLE}))) - X.UI.ClosePopupMenu() - end, - }, - X.CONSTANT.MENU_DIVIDER, - { - szOption = _L['Report bugs'], - fnAction = function() - X.OpenBrowser(X.PACKET_INFO.AUTHOR_FEEDBACK_URL) - X.UI.ClosePopupMenu() - end, - }, - } + local menu = {} if IsCtrlKeyDown() and IsAltKeyDown() and IsShiftKeyDown() then table.insert(menu, 1, { szOption = _L['Enable debug tools'], @@ -205,6 +156,7 @@ function PS.OnPanelActive(wnd) X.SetDebugging('Dev_UIManager', true) X.SetDebugging('Dev_UIFindStation', true) X.SetDebugging('Dev_DebugLogs', true) + X.SetDebugging('Dev_BgMsgViewer', true) X.OutputSystemAnnounceMessage(_L['Debug tools has been enabled...']) X.Panel.Reopen() X.UI.ClosePopupMenu() @@ -212,6 +164,65 @@ function PS.OnPanelActive(wnd) }) table.insert(menu, 2, X.CONSTANT.MENU_DIVIDER) end + if X.IsDebugging('Dev_BgMsgViewer') then + table.insert(menu, 1, { + szOption = _L['Open BgMsgViewer'], + rgb = { 128, 255, 128 }, + fnAction = function() + _G[X.NSFormatString('{$NS}_BgMsgViewer')].Open() + X.UI.ClosePopupMenu() + end, + }) + table.insert(menu, 2, X.CONSTANT.MENU_DIVIDER) + end + table.insert(menu, { + szOption = _L['Show error message'], + tip = { + render = _L['Show error message, please commit it while report bugs'], + position = X.UI.TIP_POSITION.BOTTOM_TOP, + }, + fnAction = function() + local szErrmsg = X.GetAddonErrorMessage() + local nErrmsgLen, nMaxLen = #szErrmsg, 1024 + if nErrmsgLen == 0 then + X.Alert(_L['No error message found.']) + return + end + if nErrmsgLen > 300 then + szErrmsg = szErrmsg:sub(0, nMaxLen) + .. '\n========================================' + .. '\n' .. '... ' .. (nErrmsgLen - nMaxLen) .. ' char(s) omitted.' + .. '\n========================================' + .. '\n# Full error logs:' + .. '\n> ' .. X.GetAbsolutePath(X.GetAddonErrorMessageFilePath()) + .. '\n========================================' + end + X.UI.OpenTextEditor(szErrmsg, { w = 800, h = 600, title = _L['Error message'] }) + X.UI.ClosePopupMenu() + end, + }) + table.insert(menu, { + szOption = _L['Open error message folder'], + fnAction = function() + X.OpenFolder(X.GetAbsolutePath(X.GetAddonErrorMessageFilePath())) + X.UI.ClosePopupMenu() + end, + }) + table.insert(menu, { + szOption = _L['Open logs folder'], + fnAction = function() + X.OpenFolder(X.GetAbsolutePath(X.FormatPath({'logs/', X.PATH_TYPE.ROLE}))) + X.UI.ClosePopupMenu() + end, + }) + table.insert(menu, X.CONSTANT.MENU_DIVIDER) + table.insert(menu, { + szOption = _L['Report bugs'], + fnAction = function() + X.OpenBrowser(X.PACKET_INFO.AUTHOR_FEEDBACK_URL) + X.UI.ClosePopupMenu() + end, + }) return menu end, }):AutoWidth():Width() + 5 diff --git a/Boilerplate_!Base/src/lib/Event.lua b/Boilerplate_!Base/src/lib/Event.lua index 9ecd8298b..1292347ec 100644 --- a/Boilerplate_!Base/src/lib/Event.lua +++ b/Boilerplate_!Base/src/lib/Event.lua @@ -670,13 +670,17 @@ local BG_MSG_PROGRESS_EVENT = { szName = 'BgMsgProgress' } ------------------------------------ do local BG_MSG_PART = {} +local VIEWER_NAME = X.NSFormatString('{$NS}_BgMsgViewer') +local SEGMENT_VIEWER_NAME = X.NSFormatString('{$NS}_BgMsgSegmentViewer') local function OnBgMsg() local szMsgSID, nChannel, dwID, szName, aMsg, bSelf = arg0, arg1, arg2, arg3, arg4, arg2 == X.GetClientPlayerID() if not szMsgSID or szMsgSID:sub(1, #BG_MSG_ID_PREFIX) ~= BG_MSG_ID_PREFIX or szMsgSID:sub(-#BG_MSG_ID_SUFFIX) ~= BG_MSG_ID_SUFFIX then return end local szMsgID = szMsgSID:sub(#BG_MSG_ID_PREFIX + 1, -#BG_MSG_ID_SUFFIX - 1) - if not CommonEventRegister(BG_MSG_EVENT, szMsgID) then + local bRegistered = CommonEventRegister(BG_MSG_EVENT, szMsgID) + local bDebugging = X.IsDebugging('Dev_BgMsgViewer') + if not bRegistered and not bDebugging then return end -- pagination @@ -685,6 +689,10 @@ local function OnBgMsg() BG_MSG_PART[szMsgUUID] = {} end BG_MSG_PART[szMsgUUID][nSegIndex] = X.SimpleDecryptString(X.IsString(szPart) and szPart or '') + -- hook: record segment to segment viewer + if _G[SEGMENT_VIEWER_NAME] and _G[SEGMENT_VIEWER_NAME].RecordSegment then + _G[SEGMENT_VIEWER_NAME].RecordSegment(szMsgID, szMsgUUID, nChannel, dwID, szName, nSegCount, nSegIndex, szPart) + end -- fire progress event local nSegRecv = 0 for _, _ in pairs(BG_MSG_PART[szMsgUUID]) do @@ -696,7 +704,17 @@ local function OnBgMsg() local szPlain = table.concat(BG_MSG_PART[szMsgUUID]) local aData = szPlain and X.DecodeLUAData(szPlain) if aData then - CommonEventFirer(BG_MSG_EVENT, szMsgID, aData[1], nChannel, dwID, szName, bSelf) + -- hook: record complete message to viewer + if _G[VIEWER_NAME] and _G[VIEWER_NAME].RecordMessage then + _G[VIEWER_NAME].RecordMessage(szMsgID, nChannel, dwID, szName, bSelf, aMsg, aData[1], nSegCount) + end + -- hook: mark complete in segment viewer + if _G[SEGMENT_VIEWER_NAME] and _G[SEGMENT_VIEWER_NAME].MarkComplete then + _G[SEGMENT_VIEWER_NAME].MarkComplete(szMsgUUID) + end + if bRegistered then + CommonEventFirer(BG_MSG_EVENT, szMsgID, aData[1], nChannel, dwID, szName, bSelf) + end --[[#DEBUG BEGIN]] else X.OutputDebugMessage('BG_EVENT#' .. szMsgID, X.GetTraceback('Cannot decode BgMsg: ' .. szPlain), X.DEBUG_LEVEL.ERROR) @@ -729,6 +747,7 @@ end end do +local VIEWER_NAME = X.NSFormatString('{$NS}_BgMsgViewer') local MAX_CHANNEL_LEN = setmetatable({ [PLAYER_TALK_CHANNEL.RAID] = 300, [PLAYER_TALK_CHANNEL.BATTLE_FIELD] = 300, @@ -798,6 +817,11 @@ function X.SendBgMsg(nChannel, szMsgID, oData, bSilent) } me.Talk(nChannel, szTarget, aSay) end + -- hook: record outbound message to viewer + if _G[VIEWER_NAME] and _G[VIEWER_NAME].RecordMessage then + local aMsg = { { u = szMsgUUID, c = nSegCount, i = 1 }, szArg } + _G[VIEWER_NAME].RecordMessage(szMsgID, nChannel, me.dwID, me.szName, true, aMsg, oData, nSegCount, 'OUT') + end end end end From a53b93c431f5224ecc222c0f318ede00174af9b3 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 13:31:35 +0800 Subject: [PATCH 05/15] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=B7=A5=E5=85=B7=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MY_!Base/src/Dev_BgMsgSegmentViewer.lua | 4 ++-- MY_!Base/src/Dev_BgMsgSender.lua | 4 ++-- MY_!Base/src/Dev_BgMsgViewer.lua | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MY_!Base/src/Dev_BgMsgSegmentViewer.lua b/MY_!Base/src/Dev_BgMsgSegmentViewer.lua index cb390f106..f629d2cf6 100644 --- a/MY_!Base/src/Dev_BgMsgSegmentViewer.lua +++ b/MY_!Base/src/Dev_BgMsgSegmentViewer.lua @@ -3,8 +3,8 @@ -- @desc : 背景通讯分片查看器 -- @copyright: Emil Zhai -------------------------------------------------------------------------------- ----@class (partial) Boilerplate -local X = Boilerplate +---@class (partial) MY +local X = MY -------------------------------------------------------------------------------- local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSegmentViewer') -------------------------------------------------------------------------------- diff --git a/MY_!Base/src/Dev_BgMsgSender.lua b/MY_!Base/src/Dev_BgMsgSender.lua index dbc91ea4f..a2bcacf96 100644 --- a/MY_!Base/src/Dev_BgMsgSender.lua +++ b/MY_!Base/src/Dev_BgMsgSender.lua @@ -3,8 +3,8 @@ -- @desc : 背景通讯发送器 -- @copyright: Emil Zhai -------------------------------------------------------------------------------- ----@class (partial) Boilerplate -local X = Boilerplate +---@class (partial) MY +local X = MY -------------------------------------------------------------------------------- local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSender') -------------------------------------------------------------------------------- diff --git a/MY_!Base/src/Dev_BgMsgViewer.lua b/MY_!Base/src/Dev_BgMsgViewer.lua index 8e7036dce..467cbe490 100644 --- a/MY_!Base/src/Dev_BgMsgViewer.lua +++ b/MY_!Base/src/Dev_BgMsgViewer.lua @@ -3,8 +3,8 @@ -- @desc : 背景通讯查看器 -- @copyright: Emil Zhai -------------------------------------------------------------------------------- ----@class (partial) Boilerplate -local X = Boilerplate +---@class (partial) MY +local X = MY -------------------------------------------------------------------------------- local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgViewer') -------------------------------------------------------------------------------- From 873f684509af01cf39b4ad93d6f285d0bd313975 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 14:31:35 +0800 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E9=80=9A=E8=AE=AF=E8=B0=83=E8=AF=95=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/info.ini | 10 +- Boilerplate_!Base/info.ini.zh_TW | 10 +- Boilerplate_!Base/lang/Dev/zhcn.jx3dat | 29 +++ Boilerplate_!Base/lang/Dev/zhtw.jx3dat | 29 +++ .../src/Dev_BgMsgSegmentViewer.lua | 142 ++++++----- .../src/Dev_BgMsgSegmentViewer_Detail.lua | 221 ++++++++++++++++++ Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 177 +++++++++----- .../src/Dev_BgMsgViewer_Detail.lua | 210 +++++++++++++++++ 8 files changed, 710 insertions(+), 118 deletions(-) create mode 100644 Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua create mode 100644 Boilerplate_!Base/src/Dev_BgMsgViewer_Detail.lua diff --git a/Boilerplate_!Base/info.ini b/Boilerplate_!Base/info.ini index e890d81b4..fe2828445 100644 --- a/Boilerplate_!Base/info.ini +++ b/Boilerplate_!Base/info.ini @@ -110,7 +110,9 @@ lua_102=src\Dev_UIEditor.lua lua_103=src\Dev_UIFindStation.lua lua_104=src\Dev_BgMsgSender.lua lua_105=src\Dev_BgMsgViewer.lua -lua_106=src\Dev_BgMsgSegmentViewer.lua -lua_107=src\PS.Welcome.lua -lua_108=src\PS.GlobalConfig.lua -lua_109=src\PS.UISample.lua +lua_106=src\Dev_BgMsgViewer_Detail.lua +lua_107=src\Dev_BgMsgSegmentViewer.lua +lua_108=src\Dev_BgMsgSegmentViewer_Detail.lua +lua_109=src\PS.Welcome.lua +lua_110=src\PS.GlobalConfig.lua +lua_111=src\PS.UISample.lua diff --git a/Boilerplate_!Base/info.ini.zh_TW b/Boilerplate_!Base/info.ini.zh_TW index b118302b5..8ff578173 100644 --- a/Boilerplate_!Base/info.ini.zh_TW +++ b/Boilerplate_!Base/info.ini.zh_TW @@ -110,7 +110,9 @@ lua_102=src\Dev_UIEditor.lua lua_103=src\Dev_UIFindStation.lua lua_104=src\Dev_BgMsgSender.lua lua_105=src\Dev_BgMsgViewer.lua -lua_106=src\Dev_BgMsgSegmentViewer.lua -lua_107=src\PS.Welcome.lua -lua_108=src\PS.GlobalConfig.lua -lua_109=src\PS.UISample.lua +lua_106=src\Dev_BgMsgViewer_Detail.lua +lua_107=src\Dev_BgMsgSegmentViewer.lua +lua_108=src\Dev_BgMsgSegmentViewer_Detail.lua +lua_109=src\PS.Welcome.lua +lua_110=src\PS.GlobalConfig.lua +lua_111=src\PS.UISample.lua diff --git a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat index fe192ba0e..e6bf393cb 100644 --- a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat @@ -15,6 +15,30 @@ return { ['Clear'] = '清空', ['Segment Viewer'] = '分片查看', ['BgMsg Sender'] = '消息发送', + ['Time'] = '时间', + ['Dir'] = '方向', + ['Status'] = '状态', + ['Channel'] = '频道', + ['MsgID'] = '消息ID', + ['Sender'] = '发送者', + ['Preview'] = '预览', + + -- Dev_BgMsgViewer_Detail.lua + ['BgMsgDetail'] = '背景消息详情', + ['Copy to clipboard'] = '复制到剪切板', + ['Open in TextEditor'] = '在文本编辑器中打开', + ['Detail has been copied to clipboard'] = '详情已复制到剪切板', + ['Time:'] = '时间:', + ['Direction:'] = '方向:', + ['MsgUUID:'] = '消息UUID:', + ['Channel:'] = '频道:', + ['Sender:'] = '发送者:', + ['IsSelf:'] = '是否自己:', + ['SegCount:'] = '分片数:', + ['SegIndex:'] = '分片索引:', + ['Complete:'] = '是否完整:', + ['Decoded Data:'] = '解码数据:', + ['(Not yet decoded or decode failed)'] = '(尚未解码或解码失败)', -- Dev_BgMsgSender.lua ['BgMsgSender'] = '背景消息发送', @@ -35,6 +59,11 @@ return { ['Show incomplete only'] = '仅显示未完成', ['Clear completed'] = '清除已完成', + -- Dev_BgMsgSegmentViewer_Detail.lua + ['BgMsgSegmentDetail'] = '背景消息分片详情', + ['Received:'] = '已接收:', + ['Segments:'] = '分片列表:', + -- Dev_UIManager.lua ['Dev_UIManager'] = '窗口枚举', UI_DESC = { diff --git a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat index 8b65cc292..72f19b7c2 100644 --- a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat @@ -15,6 +15,30 @@ return { ['Clear'] = '娓呯┖', ['Segment Viewer'] = '鍒嗙墖鏌ョ湅', ['BgMsg Sender'] = '娑堟伅鐧奸', + ['Time'] = '鏅傞枔', + ['Dir'] = '鏂瑰悜', + ['Status'] = '鐙鎱', + ['Channel'] = '闋婚亾', + ['MsgID'] = '娑堟伅ID', + ['Sender'] = '鐧奸佽', + ['Preview'] = '闋愯', + + -- Dev_BgMsgViewer_Detail.lua + ['BgMsgDetail'] = '鑳屾櫙娑堟伅瑭虫儏', + ['Copy to clipboard'] = '瑜囪=鍒板壀鍒囨澘', + ['Open in TextEditor'] = '鍦ㄦ枃鏈法杓櫒涓墦闁', + ['Detail has been copied to clipboard'] = '瑭虫儏宸茶瑁藉埌鍓垏鏉', + ['Time:'] = '鏅傞枔锛', + ['Direction:'] = '鏂瑰悜锛', + ['MsgUUID:'] = '娑堟伅UUID锛', + ['Channel:'] = '闋婚亾锛', + ['Sender:'] = '鐧奸佽咃細', + ['IsSelf:'] = '鏄惁鑷繁锛', + ['SegCount:'] = '鍒嗙墖鏁革細', + ['SegIndex:'] = '鍒嗙墖绱㈠紩锛', + ['Complete:'] = '鏄惁瀹屾暣锛', + ['Decoded Data:'] = '瑙g⒓鏁告摎锛', + ['(Not yet decoded or decode failed)'] = '(灏氭湭瑙g⒓鎴栬В纰煎け鏁)', -- Dev_BgMsgSender.lua ['BgMsgSender'] = '鑳屾櫙娑堟伅鐧奸', @@ -35,6 +59,11 @@ return { ['Show incomplete only'] = '鍍呴’绀烘湭瀹屾垚', ['Clear completed'] = '娓呴櫎宸插畬鎴', + -- Dev_BgMsgSegmentViewer_Detail.lua + ['BgMsgSegmentDetail'] = '鑳屾櫙娑堟伅鍒嗙墖瑭虫儏', + ['Received:'] = '宸叉帴鏀讹細', + ['Segments:'] = '鍒嗙墖鍒楄〃锛', + -- Dev_UIManager.lua ['Dev_UIManager'] = '绐楀彛鏋氳垑', UI_DESC = { diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua index cb390f106..50c8ad9bb 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua @@ -76,34 +76,40 @@ function D.RefreshList() if not frame then return end - local uiList = X.UI(frame):Fetch('WndListBox_Segments') - if not uiList:Raw() then + local uiTable = X.UI(frame):Fetch('WndTable_Segments') + if not uiTable:Raw() then return end - uiList:ListBox('clear') + local aDataSource = {} for szMsgUUID, tSeg in pairs(O.tSegments) do local bComplete = D.IsComplete(tSeg) -- 过滤:仅显示未组装的 if not O.bFilterIncomplete or not bComplete then - local szTime = X.FormatTime(tSeg.nTime, '%hh:%mm:%ss') - local szChannel = D.GetChannelName(tSeg.nChannel) local nRecv = D.GetReceivedCount(tSeg) - local szStatus = bComplete and '[OK]' or string.format('[%d/%d]', nRecv, tSeg.nSegCount) - local szText = string.format('%s %s %s %s (%s)', szTime, szStatus, szChannel, tSeg.szMsgID, tSeg.szName) - local r, g, b = 255, 255, 255 - if bComplete then - r, g, b = 128, 255, 128 - elseif nRecv < tSeg.nSegCount then - r, g, b = 255, 255, 128 - end - uiList:ListBox('insert', { - id = szMsgUUID, - text = szText, - data = tSeg, - r = r, g = g, b = b, + table.insert(aDataSource, { + szMsgUUID = szMsgUUID, + szTime = X.FormatTime(tSeg.nTime, '%hh:%mm:%ss'), + szChannel = D.GetChannelName(tSeg.nChannel), + szMsgID = tSeg.szMsgID, + szName = tSeg.szName, + nRecv = nRecv, + nSegCount = tSeg.nSegCount, + bComplete = bComplete, + tSeg = tSeg, }) end end + uiTable:DataSource(aDataSource) +end + +-- 获取记录颜色 +function D.GetRecordColor(record) + if record.bComplete then + return 128, 255, 128 + elseif record.nRecv < record.nSegCount then + return 255, 255, 128 + end + return 255, 255, 255 end -- 显示详情 @@ -111,32 +117,10 @@ function D.ShowDetail(tSeg) if not tSeg then return end - local aLines = {} - table.insert(aLines, '========== Segment Detail ==========') - table.insert(aLines, 'Time: ' .. X.FormatTime(tSeg.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) - table.insert(aLines, 'MsgID: ' .. tostring(tSeg.szMsgID)) - table.insert(aLines, 'MsgUUID: ' .. tostring(tSeg.szMsgUUID)) - table.insert(aLines, 'Channel: ' .. D.GetChannelName(tSeg.nChannel) .. ' (' .. tostring(tSeg.nChannel) .. ')') - table.insert(aLines, 'Sender: ' .. tostring(tSeg.szName) .. ' (' .. tostring(tSeg.dwID) .. ')') - table.insert(aLines, 'SegCount: ' .. tostring(tSeg.nSegCount)) - table.insert(aLines, 'Received: ' .. tostring(D.GetReceivedCount(tSeg))) - table.insert(aLines, 'Complete: ' .. tostring(D.IsComplete(tSeg))) - table.insert(aLines, '') - table.insert(aLines, '---------- Segments ----------') - for i = 1, tSeg.nSegCount do - local part = tSeg.aParts[i] - if part then - table.insert(aLines, string.format('[%d] Received at %s', i, X.FormatTime(part.nTime, '%hh:%mm:%ss'))) - table.insert(aLines, ' ' .. tostring(part.szPart)) - else - table.insert(aLines, string.format('[%d] (Missing)', i)) - end + local szDetailName = X.NSFormatString('{$NS}_BgMsgSegmentViewer_Detail') + if _G[szDetailName] and _G[szDetailName].Open then + _G[szDetailName].Open(tSeg) end - X.UI.OpenTextEditor(table.concat(aLines, '\n'), { - title = 'Segment Detail - ' .. tostring(tSeg.szMsgID), - w = 600, - h = 500, - }) end -- 打开界面 @@ -146,8 +130,8 @@ function D.Open() return end local ui = X.UI.CreateFrame(FRAME_NAME, { - w = 700, - h = 500, + w = 800, + h = 600, text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgSegmentViewer'], anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, close = true, @@ -195,16 +179,70 @@ function D.Open() D.RefreshList() end, }) - -- 列表 - ui:Append('WndListBox', { - name = 'WndListBox_Segments', + -- 表格 + ui:Append('WndTable', { + name = 'WndTable_Segments', x = 10, y = 85, w = nW - 20, h = nH - 95, + onRowLClick = function(rec, nIndex) + D.ShowDetail(rec.tSeg) + end, + columns = { + { + key = 'szTime', + title = _L['Time'], + alignHorizontal = 'center', + width = 80, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(value, 162, r, g, b) + end, + }, + { + key = 'bComplete', + title = _L['Status'], + alignHorizontal = 'center', + width = 80, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + local szStatus = value and 'OK' or string.format('%d/%d', record.nRecv, record.nSegCount) + return GetFormatText(szStatus, 162, r, g, b) + end, + }, + { + key = 'szChannel', + title = _L['Channel'], + alignHorizontal = 'center', + width = 100, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(value, 162, r, g, b) + end, + }, + { + key = 'szName', + title = _L['Sender'], + alignHorizontal = 'left', + width = 120, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(' ' .. tostring(value), 162, r, g, b) + end, + }, + { + key = 'szMsgID', + title = _L['MsgID'], + alignHorizontal = 'left', + minWidth = 300, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(' ' .. tostring(value), 162, r, g, b) + end, + }, + }, + dataSource = {}, }) - X.UI(ui:Raw()):Fetch('WndListBox_Segments'):ListBox('onlclick', function(id, text, data, selected) - D.ShowDetail(data) - end) D.RefreshList() end @@ -215,7 +253,7 @@ function D.OnResize() end local ui = X.UI(frame) local nW, nH = ui:ContainerSize() - ui:Fetch('WndListBox_Segments'):Size(nW - 20, nH - 95) + ui:Fetch('WndTable_Segments'):Size(nW - 20, nH - 95) end function D.Close() diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua new file mode 100644 index 000000000..956a51a92 --- /dev/null +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua @@ -0,0 +1,221 @@ +-------------------------------------------------------------------------------- +-- This file is part of the JX3 Plugin Project. +-- @desc : 背景通讯分片详情查看器 +-- @copyright: Emil Zhai +-------------------------------------------------------------------------------- +---@class (partial) Boilerplate +local X = Boilerplate +-------------------------------------------------------------------------------- +local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSegmentViewer_Detail') +-------------------------------------------------------------------------------- +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] +-------------------------------------------------------------------------------- +local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') +-------------------------------------------------------------------------------- + +local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgSegmentViewer_Detail') +local LABEL_WIDTH = 100 +local VALUE_WIDTH = 450 +local ROW_HEIGHT = 25 +local PADDING = 10 + +local O = {} +local D = {} + +-- 获取频道名称 +function D.GetChannelName(nChannel) + local szMsgType = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_MSG_TYPE[nChannel] + return szMsgType and g_tStrings.tChannelName[szMsgType] or tostring(nChannel) +end + +-- 获取已接收分片数 +function D.GetReceivedCount(tSeg) + local nCount = 0 + for _, _ in pairs(tSeg.aParts) do + nCount = nCount + 1 + end + return nCount +end + +-- 检查是否完整 +function D.IsComplete(tSeg) + return tSeg.bComplete or D.GetReceivedCount(tSeg) >= tSeg.nSegCount +end + +-- 生成详情文本(用于复制和TextEditor) +function D.GetDetailText(tSeg) + local aLines = {} + table.insert(aLines, '========== Segment Detail ==========') + table.insert(aLines, 'Time: ' .. X.FormatTime(tSeg.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + table.insert(aLines, 'MsgID: ' .. tostring(tSeg.szMsgID)) + table.insert(aLines, 'MsgUUID: ' .. tostring(tSeg.szMsgUUID)) + table.insert(aLines, 'Channel: ' .. D.GetChannelName(tSeg.nChannel) .. ' (' .. tostring(tSeg.nChannel) .. ')') + table.insert(aLines, 'Sender: ' .. tostring(tSeg.szName) .. ' (' .. tostring(tSeg.dwID) .. ')') + table.insert(aLines, 'SegCount: ' .. tostring(tSeg.nSegCount)) + table.insert(aLines, 'Received: ' .. tostring(D.GetReceivedCount(tSeg))) + table.insert(aLines, 'Complete: ' .. tostring(D.IsComplete(tSeg))) + table.insert(aLines, '') + table.insert(aLines, '---------- Segments ----------') + for i = 1, tSeg.nSegCount do + local part = tSeg.aParts[i] + if part then + table.insert(aLines, string.format('[%d] Received at %s', i, X.FormatTime(part.nTime, '%hh:%mm:%ss'))) + table.insert(aLines, ' ' .. tostring(part.szPart)) + else + table.insert(aLines, string.format('[%d] (Missing)', i)) + end + end + return table.concat(aLines, '\n') +end + +-- 添加表单行 +function D.AppendFormRow(ui, nY, szLabel, szValue) + ui:Append('Text', { + x = PADDING, y = nY, w = LABEL_WIDTH, h = ROW_HEIGHT, + text = szLabel, + font = 162, + alignHorizontal = 'right', + alignVertical = 'center', + }) + ui:Append('Text', { + x = PADDING + LABEL_WIDTH + 5, y = nY, w = VALUE_WIDTH, h = ROW_HEIGHT, + text = szValue, + font = 162, + alignHorizontal = 'left', + alignVertical = 'center', + }) + return nY + ROW_HEIGHT +end + +-- 打开界面 +function D.Open(tSeg) + if not tSeg then + return + end + -- 关闭已有窗口 + D.Close() + -- 保存当前记录 + O.tSeg = tSeg + -- 创建窗体 + local ui = X.UI.CreateFrame(FRAME_NAME, { + w = 600, + h = 600, + text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgSegmentDetail'], + anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, + close = true, + esc = true, + resize = true, + minimize = false, + onSizeChange = function() + D.OnResize() + end, + onSettingsClick = function() + local menu = { + { + szOption = _L['Copy to clipboard'], + fnAction = function() + local szText = D.GetDetailText(O.tSeg) + SetDataToClip(szText) + X.OutputAnnounceMessage(_L['Detail has been copied to clipboard']) + end, + }, + { + szOption = _L['Open in TextEditor'], + fnAction = function() + X.UI.OpenTextEditor(D.GetDetailText(O.tSeg), { + title = 'Segment Detail - ' .. tostring(O.tSeg.szMsgID), + w = 600, + h = 500, + }) + end, + }, + } + PopupMenu(menu) + end, + }) + local nW, nH = ui:ContainerSize() + local nY = 50 + -- 基本信息 + nY = D.AppendFormRow(ui, nY, _L['Time:'], X.FormatTime(tSeg.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + nY = D.AppendFormRow(ui, nY, _L['MsgID:'], tostring(tSeg.szMsgID)) + nY = D.AppendFormRow(ui, nY, _L['MsgUUID:'], tostring(tSeg.szMsgUUID)) + nY = D.AppendFormRow(ui, nY, _L['Channel:'], D.GetChannelName(tSeg.nChannel) .. ' (' .. tostring(tSeg.nChannel) .. ')') + nY = D.AppendFormRow(ui, nY, _L['Sender:'], tostring(tSeg.szName) .. ' (' .. tostring(tSeg.dwID) .. ')') + nY = D.AppendFormRow(ui, nY, _L['SegCount:'], tostring(tSeg.nSegCount)) + nY = D.AppendFormRow(ui, nY, _L['Received:'], tostring(D.GetReceivedCount(tSeg))) + nY = D.AppendFormRow(ui, nY, _L['Complete:'], tostring(D.IsComplete(tSeg))) + -- 分隔线 + nY = nY + 10 + ui:Append('Text', { + x = PADDING, y = nY, w = nW - PADDING * 2, h = ROW_HEIGHT, + text = _L['Segments:'], + font = 162, + alignHorizontal = 'center', + alignVertical = 'center', + }) + nY = nY + ROW_HEIGHT + -- 分片预览区域 + local aSegLines = {} + for i = 1, tSeg.nSegCount do + local part = tSeg.aParts[i] + if part then + table.insert(aSegLines, string.format('[%d] Received at %s', i, X.FormatTime(part.nTime, '%hh:%mm:%ss'))) + table.insert(aSegLines, ' ' .. tostring(part.szPart)) + else + table.insert(aSegLines, string.format('[%d] (Missing)', i)) + end + end + local szSegData = table.concat(aSegLines, '\n') + O.nEditBoxY = nY -- 保存 EditBox 的 Y 位置,用于 OnResize + ui:Append('WndEditBox', { + name = 'WndEditBox_Segments', + x = PADDING, y = nY, + w = nW - PADDING * 2, + h = nH - nY - PADDING, + multiline = true, + text = szSegData, + enable = false, + }) +end + +function D.OnResize() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local nW, nH = ui:ContainerSize() + ui:Fetch('WndEditBox_Segments'):Size(nW - PADDING * 2, nH - O.nEditBoxY - PADDING) +end + +function D.Close() + X.UI.CloseFrame(FRAME_NAME) + O.tSeg = nil +end + +function D.IsOpened() + return Station.Lookup('Normal/' .. FRAME_NAME) ~= nil +end + +-------------------------------------------------------------------------------- +-- 全局导出 +-------------------------------------------------------------------------------- +do +local settings = { + name = FRAME_NAME, + exports = { + { + preset = 'UIEvent', + fields = { + 'Open', + 'Close', + 'IsOpened', + }, + root = D, + }, + }, +} +_G[FRAME_NAME] = X.CreateModule(settings) +end + +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'FINISH')--[[#DEBUG END]] diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index 8e7036dce..582f9591b 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -86,32 +86,33 @@ function D.RefreshList() if not frame then return end - local uiList = X.UI(frame):Fetch('WndListBox_History') - if not uiList:Raw() then + local uiTable = X.UI(frame):Fetch('WndTable_History') + if not uiTable:Raw() then return end - uiList:ListBox('clear') + local aDataSource = {} for i, rec in ipairs(O.aHistory) do - local szTime = X.FormatTime(rec.nTime, '%hh:%mm:%ss') - local szChannel = D.GetChannelName(rec.nChannel) - local szStatus = rec.bComplete and '[OK]' or '[..]' - local szDir = rec.szDirection == 'OUT' and '[OUT]' or '[IN]' - local szText = string.format('%s %s %s %s %s (%s)', szTime, szDir, szStatus, szChannel, rec.szMsgID, rec.szName) - local r, g, b = 255, 255, 255 - if rec.szDirection == 'OUT' then - r, g, b = 128, 200, 255 - elseif rec.bSelf then - r, g, b = 128, 255, 128 - elseif not rec.bComplete then - r, g, b = 255, 255, 128 + local szPreview = '' + if rec.oData ~= nil then + szPreview = X.EncodeLUAData(rec.oData) + if #szPreview > 100 then + szPreview = szPreview:sub(1, 100) .. '...' + end end - uiList:ListBox('insert', { - id = i, - text = szText, - data = rec, - r = r, g = g, b = b, + table.insert(aDataSource, { + nIndex = i, + szTime = X.FormatTime(rec.nTime, '%hh:%mm:%ss'), + szDirection = rec.szDirection or 'IN', + bComplete = rec.bComplete, + szChannel = D.GetChannelName(rec.nChannel), + szMsgID = rec.szMsgID, + szName = rec.szName, + szPreview = szPreview, + bSelf = rec.bSelf, + rec = rec, }) end + uiTable:DataSource(aDataSource) end -- 显示详情 @@ -119,36 +120,10 @@ function D.ShowDetail(rec) if not rec then return end - local aLines = {} - table.insert(aLines, '========== BgMsg Detail ==========') - table.insert(aLines, 'Time: ' .. X.FormatTime(rec.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) - table.insert(aLines, 'Direction: ' .. tostring(rec.szDirection or 'IN')) - table.insert(aLines, 'MsgID: ' .. tostring(rec.szMsgID)) - table.insert(aLines, 'MsgUUID: ' .. tostring(rec.szMsgUUID)) - table.insert(aLines, 'Channel: ' .. D.GetChannelName(rec.nChannel) .. ' (' .. tostring(rec.nChannel) .. ')') - if rec.szTarget then - table.insert(aLines, 'Target: ' .. tostring(rec.szTarget)) - end - table.insert(aLines, 'Sender: ' .. tostring(rec.szName) .. ' (' .. tostring(rec.dwID) .. ')') - table.insert(aLines, 'IsSelf: ' .. tostring(rec.bSelf)) - table.insert(aLines, 'SegCount: ' .. tostring(rec.nSegCount)) - table.insert(aLines, 'SegIndex: ' .. tostring(rec.nSegIndex)) - table.insert(aLines, 'Complete: ' .. tostring(rec.bComplete)) - table.insert(aLines, '') - table.insert(aLines, '---------- Raw Part ----------') - table.insert(aLines, tostring(rec.szPart)) - table.insert(aLines, '') - table.insert(aLines, '---------- Decoded Data ----------') - if rec.oData ~= nil then - table.insert(aLines, X.EncodeLUAData(rec.oData, ' ')) - else - table.insert(aLines, '(Not yet decoded or decode failed)') + local szDetailName = X.NSFormatString('{$NS}_BgMsgViewer_Detail') + if _G[szDetailName] and _G[szDetailName].Open then + _G[szDetailName].Open(rec) end - X.UI.OpenTextEditor(table.concat(aLines, '\n'), { - title = 'BgMsg Detail - ' .. tostring(rec.szMsgID), - w = 600, - h = 500, - }) end -- 打开界面 @@ -158,8 +133,8 @@ function D.Open() return end local ui = X.UI.CreateFrame(FRAME_NAME, { - w = 700, - h = 500, + w = 1200, + h = 700, text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgViewer'], anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, close = true, @@ -218,19 +193,105 @@ function D.Open() end end, }) - -- 列表 - ui:Append('WndListBox', { - name = 'WndListBox_History', + -- 表格 + ui:Append('WndTable', { + name = 'WndTable_History', x = 10, y = 85, w = nW - 20, h = nH - 95, + onRowLClick = function(rec, nIndex) + D.ShowDetail(rec.rec) + end, + columns = { + { + key = 'szTime', + title = _L['Time'], + alignHorizontal = 'center', + width = 80, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(value, 162, r, g, b) + end, + }, + { + key = 'szDirection', + title = _L['Dir'], + alignHorizontal = 'center', + width = 60, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(value, 162, r, g, b) + end, + }, + { + key = 'bComplete', + title = _L['Status'], + alignHorizontal = 'center', + width = 60, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + local szStatus = value and 'OK' or '..' + return GetFormatText(szStatus, 162, r, g, b) + end, + }, + { + key = 'szChannel', + title = _L['Channel'], + alignHorizontal = 'center', + width = 100, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(value, 162, r, g, b) + end, + }, + { + key = 'szName', + title = _L['Sender'], + alignHorizontal = 'left', + width = 120, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(' ' .. tostring(value), 162, r, g, b) + end, + }, + { + key = 'szMsgID', + title = _L['MsgID'], + alignHorizontal = 'left', + width = 200, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(' ' .. tostring(value), 162, r, g, b) + end, + }, + { + key = 'szPreview', + title = _L['Preview'], + alignHorizontal = 'left', + minWidth = 200, + render = function(value, record) + local r, g, b = D.GetRecordColor(record) + return GetFormatText(' ' .. tostring(value), 162, r, g, b) + end, + }, + }, + dataSource = {}, }) - X.UI(ui:Raw()):Fetch('WndListBox_History'):ListBox('onlclick', function(id, text, data, selected) - D.ShowDetail(data) - end) D.RefreshList() end +-- 获取记录颜色 +function D.GetRecordColor(record) + if record.szDirection == 'OUT' then + return 128, 200, 255 + elseif record.bSelf then + return 128, 255, 128 + elseif not record.bComplete then + return 255, 255, 128 + end + return 255, 255, 255 +end + function D.OnResize() local frame = Station.Lookup('Normal/' .. FRAME_NAME) if not frame then @@ -238,7 +299,7 @@ function D.OnResize() end local ui = X.UI(frame) local nW, nH = ui:ContainerSize() - ui:Fetch('WndListBox_History'):Size(nW - 20, nH - 95) + ui:Fetch('WndTable_History'):Size(nW - 20, nH - 95) end function D.Close() diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer_Detail.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer_Detail.lua new file mode 100644 index 000000000..b65ae99a7 --- /dev/null +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer_Detail.lua @@ -0,0 +1,210 @@ +-------------------------------------------------------------------------------- +-- This file is part of the JX3 Plugin Project. +-- @desc : 背景通讯详情查看器 +-- @copyright: Emil Zhai +-------------------------------------------------------------------------------- +---@class (partial) Boilerplate +local X = Boilerplate +-------------------------------------------------------------------------------- +local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgViewer_Detail') +-------------------------------------------------------------------------------- +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] +-------------------------------------------------------------------------------- +local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') +-------------------------------------------------------------------------------- + +local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgViewer_Detail') +local LABEL_WIDTH = 100 +local VALUE_WIDTH = 450 +local ROW_HEIGHT = 25 +local PADDING = 10 + +local O = {} +local D = {} + +-- 获取频道名称 +function D.GetChannelName(nChannel) + local szMsgType = X.CONSTANT.PLAYER_TALK_CHANNEL_TO_MSG_TYPE[nChannel] + return szMsgType and g_tStrings.tChannelName[szMsgType] or tostring(nChannel) +end + +-- 生成详情文本(用于复制和TextEditor) +function D.GetDetailText(rec) + local aLines = {} + table.insert(aLines, '========== BgMsg Detail ==========') + table.insert(aLines, 'Time: ' .. X.FormatTime(rec.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + table.insert(aLines, 'Direction: ' .. tostring(rec.szDirection or 'IN')) + table.insert(aLines, 'MsgID: ' .. tostring(rec.szMsgID)) + table.insert(aLines, 'MsgUUID: ' .. tostring(rec.szMsgUUID)) + table.insert(aLines, 'Channel: ' .. D.GetChannelName(rec.nChannel) .. ' (' .. tostring(rec.nChannel) .. ')') + if rec.szTarget then + table.insert(aLines, 'Target: ' .. tostring(rec.szTarget)) + end + table.insert(aLines, 'Sender: ' .. tostring(rec.szName) .. ' (' .. tostring(rec.dwID) .. ')') + table.insert(aLines, 'IsSelf: ' .. tostring(rec.bSelf)) + table.insert(aLines, 'SegCount: ' .. tostring(rec.nSegCount)) + table.insert(aLines, 'SegIndex: ' .. tostring(rec.nSegIndex)) + table.insert(aLines, 'Complete: ' .. tostring(rec.bComplete)) + table.insert(aLines, '') + table.insert(aLines, '---------- Raw Part ----------') + table.insert(aLines, tostring(rec.szPart)) + table.insert(aLines, '') + table.insert(aLines, '---------- Decoded Data ----------') + if rec.oData ~= nil then + table.insert(aLines, X.EncodeLUAData(rec.oData, ' ')) + else + table.insert(aLines, '(Not yet decoded or decode failed)') + end + return table.concat(aLines, '\n') +end + +-- 添加表单行 +function D.AppendFormRow(ui, nY, szLabel, szValue) + ui:Append('Text', { + x = PADDING, y = nY, w = LABEL_WIDTH, h = ROW_HEIGHT, + text = szLabel, + font = 162, + alignHorizontal = 'right', + alignVertical = 'center', + }) + ui:Append('Text', { + x = PADDING + LABEL_WIDTH + 5, y = nY, w = VALUE_WIDTH, h = ROW_HEIGHT, + text = szValue, + font = 162, + alignHorizontal = 'left', + alignVertical = 'center', + }) + return nY + ROW_HEIGHT +end + +-- 打开界面 +function D.Open(rec) + if not rec then + return + end + -- 关闭已有窗口 + D.Close() + -- 保存当前记录 + O.rec = rec + -- 创建窗体 + local ui = X.UI.CreateFrame(FRAME_NAME, { + w = 600, + h = 600, + text = X.PACKET_INFO.NAME .. g_tStrings.STR_CONNECT .. _L['BgMsgDetail'], + anchor = { s = 'CENTER', r = 'CENTER', x = 0, y = 0 }, + close = true, + esc = true, + resize = true, + minimize = false, + onSizeChange = function() + D.OnResize() + end, + onSettingsClick = function() + local menu = { + { + szOption = _L['Copy to clipboard'], + fnAction = function() + local szText = D.GetDetailText(O.rec) + SetDataToClip(szText) + X.OutputAnnounceMessage(_L['Detail has been copied to clipboard']) + end, + }, + { + szOption = _L['Open in TextEditor'], + fnAction = function() + X.UI.OpenTextEditor(D.GetDetailText(O.rec), { + title = 'BgMsg Detail - ' .. tostring(O.rec.szMsgID), + w = 600, + h = 500, + }) + end, + }, + } + PopupMenu(menu) + end, + }) + local nW, nH = ui:ContainerSize() + local nY = 50 + -- 基本信息 + nY = D.AppendFormRow(ui, nY, _L['Time:'], X.FormatTime(rec.nTime, '%yyyy-%MM-%dd %hh:%mm:%ss')) + nY = D.AppendFormRow(ui, nY, _L['Direction:'], tostring(rec.szDirection or 'IN')) + nY = D.AppendFormRow(ui, nY, _L['MsgID:'], tostring(rec.szMsgID)) + nY = D.AppendFormRow(ui, nY, _L['MsgUUID:'], tostring(rec.szMsgUUID)) + nY = D.AppendFormRow(ui, nY, _L['Channel:'], D.GetChannelName(rec.nChannel) .. ' (' .. tostring(rec.nChannel) .. ')') + if rec.szTarget then + nY = D.AppendFormRow(ui, nY, _L['Target:'], tostring(rec.szTarget)) + end + nY = D.AppendFormRow(ui, nY, _L['Sender:'], tostring(rec.szName) .. ' (' .. tostring(rec.dwID) .. ')') + nY = D.AppendFormRow(ui, nY, _L['IsSelf:'], tostring(rec.bSelf)) + nY = D.AppendFormRow(ui, nY, _L['SegCount:'], tostring(rec.nSegCount)) + nY = D.AppendFormRow(ui, nY, _L['SegIndex:'], tostring(rec.nSegIndex)) + nY = D.AppendFormRow(ui, nY, _L['Complete:'], tostring(rec.bComplete)) + -- 分隔线 + nY = nY + 10 + ui:Append('Text', { + x = PADDING, y = nY, w = nW - PADDING * 2, h = ROW_HEIGHT, + text = _L['Decoded Data:'], + font = 162, + alignHorizontal = 'center', + alignVertical = 'center', + }) + nY = nY + ROW_HEIGHT + -- 数据预览区域 + local szData = '' + if rec.oData ~= nil then + szData = X.EncodeLUAData(rec.oData, ' ') + else + szData = _L['(Not yet decoded or decode failed)'] + end + O.nEditBoxY = nY -- 保存 EditBox 的 Y 位置,用于 OnResize + ui:Append('WndEditBox', { + name = 'WndEditBox_Data', + x = PADDING, y = nY, + w = nW - PADDING * 2, + h = nH - nY - PADDING, + multiline = true, + text = szData, + }) +end + +function D.OnResize() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local nW, nH = ui:ContainerSize() + ui:Fetch('WndEditBox_Data'):Size(nW - PADDING * 2, nH - O.nEditBoxY - PADDING) +end + +function D.Close() + X.UI.CloseFrame(FRAME_NAME) + O.rec = nil +end + +function D.IsOpened() + return Station.Lookup('Normal/' .. FRAME_NAME) ~= nil +end + +-------------------------------------------------------------------------------- +-- 全局导出 +-------------------------------------------------------------------------------- +do +local settings = { + name = FRAME_NAME, + exports = { + { + preset = 'UIEvent', + fields = { + 'Open', + 'Close', + 'IsOpened', + }, + root = D, + }, + }, +} +_G[FRAME_NAME] = X.CreateModule(settings) +end + +--[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'FINISH')--[[#DEBUG END]] From 839e5f4be71cdd954e8e62abb38c2c9569829b77 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 14:51:10 +0800 Subject: [PATCH 07/15] =?UTF-8?q?feat:=20=E8=83=8C=E6=99=AF=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E5=A2=9E=E5=8A=A0=E9=87=8D=E6=94=BE=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/lang/Dev/zhcn.jx3dat | 2 + Boilerplate_!Base/lang/Dev/zhtw.jx3dat | 2 + .../src/Dev_BgMsgSegmentViewer.lua | 5 +- Boilerplate_!Base/src/Dev_BgMsgSender.lua | 19 ++++++- Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 51 ++++++++++++++++--- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat index e6bf393cb..ac1e20cbb 100644 --- a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat @@ -22,6 +22,8 @@ return { ['MsgID'] = '消息ID', ['Sender'] = '发送者', ['Preview'] = '预览', + ['Replay'] = '重放', + ['View Detail'] = '查看详情', -- Dev_BgMsgViewer_Detail.lua ['BgMsgDetail'] = '背景消息详情', diff --git a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat index 72f19b7c2..4af577a75 100644 --- a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat @@ -22,6 +22,8 @@ return { ['MsgID'] = '娑堟伅ID', ['Sender'] = '鐧奸佽', ['Preview'] = '闋愯', + ['Replay'] = '閲嶆斁', + ['View Detail'] = '鏌ョ湅瑭虫儏', -- Dev_BgMsgViewer_Detail.lua ['BgMsgDetail'] = '鑳屾櫙娑堟伅瑭虫儏', diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua index 50c8ad9bb..8d29d2180 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua @@ -117,10 +117,7 @@ function D.ShowDetail(tSeg) if not tSeg then return end - local szDetailName = X.NSFormatString('{$NS}_BgMsgSegmentViewer_Detail') - if _G[szDetailName] and _G[szDetailName].Open then - _G[szDetailName].Open(tSeg) - end + _G[X.NSFormatString('{$NS}_BgMsgSegmentViewer_Detail')].Open(tSeg) end -- 打开界面 diff --git a/Boilerplate_!Base/src/Dev_BgMsgSender.lua b/Boilerplate_!Base/src/Dev_BgMsgSender.lua index dbc91ea4f..c9a66f447 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgSender.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgSender.lua @@ -76,10 +76,25 @@ function D.SendMessage() end -- 打开界面 -function D.Open() +-- tParams: { nChannel, szTarget, szMsgID, szData } +function D.Open(tParams) if D.IsOpened() then D.Close() - return + end + -- 如果有参数,预填充数据 + if X.IsTable(tParams) then + if tParams.nChannel ~= nil then + O.nChannel = tParams.nChannel + end + if tParams.szTarget ~= nil then + O.szTarget = tParams.szTarget + end + if tParams.szMsgID ~= nil then + O.szMsgID = tParams.szMsgID + end + if tParams.szData ~= nil then + O.szData = tParams.szData + end end local ui = X.UI.CreateFrame(FRAME_NAME, { w = 500, diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index 582f9591b..2b31f715b 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -175,10 +175,7 @@ function D.Open() x = nX, y = 50, w = 120, h = 25, text = _L['Segment Viewer'], onClick = function() - local szSegmentViewerName = X.NSFormatString('{$NS}_BgMsgSegmentViewer') - if _G[szSegmentViewerName] and _G[szSegmentViewerName].Open then - _G[szSegmentViewerName].Open() - end + _G[X.NSFormatString('{$NS}_BgMsgSegmentViewer')].Open() end, }) nX = nX + 130 @@ -187,10 +184,7 @@ function D.Open() x = nX, y = 50, w = 120, h = 25, text = _L['BgMsg Sender'], onClick = function() - local szSenderName = X.NSFormatString('{$NS}_BgMsgSender') - if _G[szSenderName] and _G[szSenderName].Open then - _G[szSenderName].Open() - end + _G[X.NSFormatString('{$NS}_BgMsgSender')].Open() end, }) -- 表格 @@ -202,6 +196,47 @@ function D.Open() onRowLClick = function(rec, nIndex) D.ShowDetail(rec.rec) end, + onRowRClick = function(rec, nIndex) + if not rec or not rec.rec then + return + end + local r = rec.rec + local menu = { + { + szOption = _L['Replay'], + fnAction = function() + local szData = '' + if r.oData ~= nil then + szData = X.EncodeLUAData(r.oData, ' ') + end + -- 处理频道和目标 + local nChannel = type(r.nChannel) == 'number' and r.nChannel or PLAYER_TALK_CHANNEL.WHISPER + local szTarget = r.szTarget or '' + -- 如果是密聊且目标为空,用发送者名字作为目标(回复) + if X.IsEmpty(szTarget) and nChannel == PLAYER_TALK_CHANNEL.WHISPER then + szTarget = r.szName or '' + end + -- 如果原始频道是字符串(密聊目标名),也用它作为目标 + if type(r.nChannel) == 'string' then + szTarget = r.nChannel + end + _G[X.NSFormatString('{$NS}_BgMsgSender')].Open({ + nChannel = nChannel, + szTarget = szTarget, + szMsgID = r.szMsgID or '', + szData = szData, + }) + end, + }, + { + szOption = _L['View Detail'], + fnAction = function() + D.ShowDetail(r) + end, + }, + } + PopupMenu(menu) + end, columns = { { key = 'szTime', From f3ce2f6464594482e165057b6da7c1693e62eee1 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 14:55:38 +0800 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MY_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua | 4 ++-- MY_!Base/src/Dev_BgMsgViewer_Detail.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MY_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua b/MY_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua index 956a51a92..090e60ba4 100644 --- a/MY_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua +++ b/MY_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua @@ -3,8 +3,8 @@ -- @desc : 背景通讯分片详情查看器 -- @copyright: Emil Zhai -------------------------------------------------------------------------------- ----@class (partial) Boilerplate -local X = Boilerplate +---@class (partial) MY +local X = MY -------------------------------------------------------------------------------- local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgSegmentViewer_Detail') -------------------------------------------------------------------------------- diff --git a/MY_!Base/src/Dev_BgMsgViewer_Detail.lua b/MY_!Base/src/Dev_BgMsgViewer_Detail.lua index b65ae99a7..260551259 100644 --- a/MY_!Base/src/Dev_BgMsgViewer_Detail.lua +++ b/MY_!Base/src/Dev_BgMsgViewer_Detail.lua @@ -3,8 +3,8 @@ -- @desc : 背景通讯详情查看器 -- @copyright: Emil Zhai -------------------------------------------------------------------------------- ----@class (partial) Boilerplate -local X = Boilerplate +---@class (partial) MY +local X = MY -------------------------------------------------------------------------------- local MODULE_PATH = X.NSFormatString('{$NS}_!Base/Dev_BgMsgViewer_Detail') -------------------------------------------------------------------------------- From 8b7fafbea149fb262e9079ed15e727789eb118ac Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 14:58:50 +0800 Subject: [PATCH 09/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E9=80=9A=E8=AE=AF=E5=88=86=E7=89=87=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=B8=8D=E8=83=BD=E7=BF=BB=E9=A1=B5=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua index 956a51a92..517405f0f 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer_Detail.lua @@ -174,7 +174,6 @@ function D.Open(tSeg) h = nH - nY - PADDING, multiline = true, text = szSegData, - enable = false, }) end From ef14f8c39e55d8264a61ae218852caa9dfe36a59 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 15:23:51 +0800 Subject: [PATCH 10/15] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B6=88?= =?UTF-8?q?=E6=81=AFID=E8=B6=85=E5=87=BA=E7=95=8C=E9=9D=A2=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua | 1 + Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua index 8d29d2180..85b996e3c 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgSegmentViewer.lua @@ -232,6 +232,7 @@ function D.Open() title = _L['MsgID'], alignHorizontal = 'left', minWidth = 300, + overflow = 'hidden', render = function(value, record) local r, g, b = D.GetRecordColor(record) return GetFormatText(' ' .. tostring(value), 162, r, g, b) diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index 2b31f715b..b7d3d6fea 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -294,6 +294,7 @@ function D.Open() title = _L['MsgID'], alignHorizontal = 'left', width = 200, + overflow = 'hidden', render = function(value, record) local r, g, b = D.GetRecordColor(record) return GetFormatText(' ' .. tostring(value), 162, r, g, b) From b1acf4753d826548815433ec67f7e5b1ccbe00a1 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 15:24:22 +0800 Subject: [PATCH 11/15] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=9F=93=E8=89=B2=E6=8F=90=E7=A4=BA=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/lang/Dev/zhcn.jx3dat | 4 ++++ Boilerplate_!Base/lang/Dev/zhtw.jx3dat | 4 ++++ Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat index ac1e20cbb..3cb1947d4 100644 --- a/Boilerplate_!Base/lang/Dev/zhcn.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhcn.jx3dat @@ -24,6 +24,10 @@ return { ['Preview'] = '预览', ['Replay'] = '重放', ['View Detail'] = '查看详情', + ['Blue: Outbound message'] = '蓝色:出站消息', + ['Green: Inbound message from self'] = '绿色:入站消息(自己发送)', + ['Yellow: Incomplete message'] = '黄色:入站消息(未完整消息)', + ['White: Inbound message from others'] = '白色:入站消息(他人发送)', -- Dev_BgMsgViewer_Detail.lua ['BgMsgDetail'] = '背景消息详情', diff --git a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat index 4af577a75..468017269 100644 --- a/Boilerplate_!Base/lang/Dev/zhtw.jx3dat +++ b/Boilerplate_!Base/lang/Dev/zhtw.jx3dat @@ -24,6 +24,10 @@ return { ['Preview'] = '闋愯', ['Replay'] = '閲嶆斁', ['View Detail'] = '鏌ョ湅瑭虫儏', + ['Blue: Outbound message'] = '钘嶈壊锛氬嚭绔欐秷鎭', + ['Green: Inbound message from self'] = '缍犺壊锛氬叆绔欐秷鎭紙鑷繁鐧奸侊級', + ['Yellow: Incomplete message'] = '榛冭壊锛氬叆绔欐秷鎭紙鏈畬鏁存秷鎭級', + ['White: Inbound message from others'] = '鐧借壊锛氬叆绔欐秷鎭紙浠栦汉鐧奸侊級', -- Dev_BgMsgViewer_Detail.lua ['BgMsgDetail'] = '鑳屾櫙娑堟伅瑭虫儏', diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index b7d3d6fea..a66c42498 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -187,6 +187,20 @@ function D.Open() _G[X.NSFormatString('{$NS}_BgMsgSender')].Open() end, }) + -- 帮助按钮(右对齐) + ui:Append('WndButton', { + name = 'WndButton_Help', + x = nW - 30, y = 50, w = 20, h = 20, + buttonStyle = 'QUESTION', + tip = { + render = GetFormatText(_L['Blue: Outbound message'], 162, 128, 200, 255) .. GetFormatText('\n') + .. GetFormatText(_L['Green: Inbound message from self'], 162, 128, 255, 128) .. GetFormatText('\n') + .. GetFormatText(_L['Yellow: Incomplete message'], 162, 255, 255, 128) .. GetFormatText('\n') + .. GetFormatText(_L['White: Inbound message from others'], 162, 255, 255, 255), + rich = true, + position = X.UI.TIP_POSITION.TOP_BOTTOM, + }, + }) -- 表格 ui:Append('WndTable', { name = 'WndTable_History', @@ -336,6 +350,7 @@ function D.OnResize() local ui = X.UI(frame) local nW, nH = ui:ContainerSize() ui:Fetch('WndTable_History'):Size(nW - 20, nH - 95) + ui:Fetch('WndButton_Help'):Pos(nW - 30, 50) end function D.Close() From 56e686982c3c040398f860ce083b1d655f6ad5a3 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 15:36:24 +0800 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=E7=95=8C=E9=9D=A2=E5=BA=93?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20WndTable=20=E7=9A=84=E7=A6=81=E7=94=A8?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/lib/UI.lua | 88 +++++++++++++++++++------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/Boilerplate_!Base/src/lib/UI.lua b/Boilerplate_!Base/src/lib/UI.lua index d06f8d91a..72ef7bd5a 100644 --- a/Boilerplate_!Base/src/lib/UI.lua +++ b/Boilerplate_!Base/src/lib/UI.lua @@ -2880,42 +2880,58 @@ local function SetComponentEnable(raw, bEnable) if bEnabled == bEnable then return end - -- make gray - local txt = GetComponentElement(raw, 'TEXT') - if txt then - local r, g, b = txt:GetFontColor() - local ratio = bEnable and 2.2 or (1 / 2.2) - if math.max(r, g, b) * ratio > 255 then - ratio = 255 / math.max(r, g, b) - end - txt:SetFontColor(math.ceil(r * ratio), math.ceil(g * ratio), math.ceil(b * ratio)) - end - -- make gray - local sha = GetComponentElement(raw, 'SHADOW') - if sha then - local r, g, b = sha:GetColorRGB() - local ratio = bEnable and 2.2 or (1 / 2.2) - if math.max(r, g, b) * ratio > 255 then - ratio = 255 / math.max(r, g, b) - end - sha:SetColorRGB(math.ceil(r * ratio), math.ceil(g * ratio), math.ceil(b * ratio)) - end - -- set sub elements enable - local combo = GetComponentElement(raw, 'COMBO_BOX') - if combo then - combo:Enable(bEnable) - end - local slider = GetComponentElement(raw, 'SLIDER') - if slider then - slider:Enable(bEnable) - end - local edit = GetComponentElement(raw, 'EDIT') - if edit then - edit:Enable(bEnable) - end - -- set enable - if raw.Enable then - raw:Enable(bEnable) + -- WndTable: set gray filter on header and content + if GetComponentType(raw) == 'WndTable' then + local hTotal = raw:Lookup('', '') + if hTotal then + hTotal:SetAlpha(bEnable and 255 or 128) + end + local scrollX = raw:Lookup('Scroll_X') + if scrollX then + scrollX:Enable(bEnable) + end + local scrollY = raw:Lookup('Scroll_Y') + if scrollY then + scrollY:Enable(bEnable) + end + else + -- make gray + local txt = GetComponentElement(raw, 'TEXT') + if txt then + local r, g, b = txt:GetFontColor() + local ratio = bEnable and 2.2 or (1 / 2.2) + if math.max(r, g, b) * ratio > 255 then + ratio = 255 / math.max(r, g, b) + end + txt:SetFontColor(math.ceil(r * ratio), math.ceil(g * ratio), math.ceil(b * ratio)) + end + -- make gray + local sha = GetComponentElement(raw, 'SHADOW') + if sha then + local r, g, b = sha:GetColorRGB() + local ratio = bEnable and 2.2 or (1 / 2.2) + if math.max(r, g, b) * ratio > 255 then + ratio = 255 / math.max(r, g, b) + end + sha:SetColorRGB(math.ceil(r * ratio), math.ceil(g * ratio), math.ceil(b * ratio)) + end + -- set sub elements enable + local combo = GetComponentElement(raw, 'COMBO_BOX') + if combo then + combo:Enable(bEnable) + end + local slider = GetComponentElement(raw, 'SLIDER') + if slider then + slider:Enable(bEnable) + end + local edit = GetComponentElement(raw, 'EDIT') + if edit then + edit:Enable(bEnable) + end + -- set enable + if raw.Enable then + raw:Enable(bEnable) + end end SetComponentProp(raw, 'bEnable', bEnable) end From 5f361bae2cc1f574d60b8f1ddb360c36c99391d7 Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 15:37:14 +0800 Subject: [PATCH 13/15] =?UTF-8?q?feat:=20=E8=83=8C=E6=99=AF=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E8=B0=83=E8=AF=95=E5=99=A8=E4=BC=98=E5=8C=96=E7=A6=81?= =?UTF-8?q?=E7=94=A8=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index a66c42498..10ff019ac 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -156,6 +156,7 @@ function D.Open() onCheck = function(bChecked) O.bRecording = bChecked D.SaveStorage() + D.UpdateEnableState() end, }) nX = nX + 100 @@ -328,6 +329,21 @@ function D.Open() dataSource = {}, }) D.RefreshList() + D.UpdateEnableState() +end + +-- 更新组件启用状态 +function D.UpdateEnableState() + local frame = Station.Lookup('Normal/' .. FRAME_NAME) + if not frame then + return + end + local ui = X.UI(frame) + local bEnable = O.bRecording + ui:Fetch('WndButton_Clear'):Enable(bEnable) + ui:Fetch('WndButton_Segment'):Enable(bEnable) + ui:Fetch('WndButton_Sender'):Enable(bEnable) + ui:Fetch('WndTable_History'):Enable(bEnable) end -- 获取记录颜色 From 5f9f960a7ea05d6047c652505b8165b8c59bbc9c Mon Sep 17 00:00:00 2001 From: Emil Zhai Date: Tue, 27 Jan 2026 15:42:11 +0800 Subject: [PATCH 14/15] =?UTF-8?q?feat:=20=E8=83=8C=E6=99=AF=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E8=B0=83=E8=AF=95=E5=99=A8=E6=8C=81=E4=B9=85=E5=8C=96?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=A2=9E=E5=8A=A0=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boilerplate_!Base/src/Dev_BgMsgViewer.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua index 10ff019ac..fe88c2c4e 100644 --- a/Boilerplate_!Base/src/Dev_BgMsgViewer.lua +++ b/Boilerplate_!Base/src/Dev_BgMsgViewer.lua @@ -16,6 +16,7 @@ local _L = X.LoadLangPack(X.PACKET_INFO.FRAMEWORK_ROOT .. '/lang/Dev/') local FRAME_NAME = X.NSFormatString('{$NS}_BgMsgViewer') local STORAGE_FILE = {'temporary/bgmsg_viewer.jx3dat', X.PATH_TYPE.ROLE} local MAX_HISTORY = 1000 +local MAX_STORAGE_HISTORY = 200 local O = { bRecording = false, @@ -34,9 +35,18 @@ end -- 保存持久化数据 function D.SaveStorage() + -- 只保留最新的 MAX_STORAGE_HISTORY 条记录 + local aHistory = O.aHistory + if #aHistory > MAX_STORAGE_HISTORY then + local aStorageHistory = {} + for i = #aHistory - MAX_STORAGE_HISTORY + 1, #aHistory do + table.insert(aStorageHistory, aHistory[i]) + end + aHistory = aStorageHistory + end X.SaveLUAData(STORAGE_FILE, { bRecording = O.bRecording, - aHistory = O.aHistory, + aHistory = aHistory, }) end From 1be9c3718466fb7f641faf59097d854725e9d70a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jan 2026 08:15:37 +0000 Subject: [PATCH 15/15] release: 29.0.2 --- CHANGELOG.md | 4 ++++ MY_!Base/src/lib/Base.lua | 4 ++-- MY_Chat/src/MY_AutoHideChat.lua | 2 +- MY_Chat/src/MY_Chat.lua | 2 +- MY_Chat/src/MY_ChatBlock.lua | 2 +- MY_Chat/src/MY_ChatCopy.lua | 2 +- MY_Chat/src/MY_ChatEmotion.lua | 2 +- MY_Chat/src/MY_ChatFilter.lua | 2 +- MY_Chat/src/MY_ChatMonitor.lua | 2 +- MY_Chat/src/MY_ChatMosaics.lua | 2 +- MY_Chat/src/MY_ChatSwitch.lua | 2 +- MY_Chat/src/MY_TalkEx.lua | 2 +- MY_Chat/src/MY_TeamBalloon.lua | 2 +- MY_Chat/src/MY_WhisperMention.lua | 2 +- MY_Chat/src/PS.lua | 2 +- MY_Font/src/MY_Font.lua | 2 +- MY_FontResource/src/MY_FontResource.lua | 2 +- 17 files changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 981dfb5f5..577667e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 鏇存柊鏃ュ織 +## 鑼椾紛鎻掍欢闆 v29.0.2 + +* [鑱婂ぉ杩囨护] 淇鑱婂ぉ杩囨护鎶ラ敊鐨勯棶棰 + ## 鑼椾紛鎻掍欢闆 v29.0.1 * [鍥㈤槦宸ュ叿] 淇閲嶄激璁板綍鏄剧ず闂 diff --git a/MY_!Base/src/lib/Base.lua b/MY_!Base/src/lib/Base.lua index 02816f642..ee0075d80 100644 --- a/MY_!Base/src/lib/Base.lua +++ b/MY_!Base/src/lib/Base.lua @@ -56,8 +56,8 @@ local IETF_BCP_47 = { } local _NAME_SPACE_ = 'MY' -local _BUILD_ = '20260121' -local _VERSION_ = '29.0.1' +local _BUILD_ = '20260127' +local _VERSION_ = '29.0.2' local _MENU_COLOR_ = {255, 165, 79} local _INTERFACE_ROOT_ = 'Interface/' local _ADDON_ROOT_ = _INTERFACE_ROOT_ .. _NAME_SPACE_ .. '/' diff --git a/MY_Chat/src/MY_AutoHideChat.lua b/MY_Chat/src/MY_AutoHideChat.lua index 35c3de215..eef186e4e 100644 --- a/MY_Chat/src/MY_AutoHideChat.lua +++ b/MY_Chat/src/MY_AutoHideChat.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_Chat' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_Chat.lua b/MY_Chat/src/MY_Chat.lua index 22d71cd21..6e8faa9a4 100644 --- a/MY_Chat/src/MY_Chat.lua +++ b/MY_Chat/src/MY_Chat.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatCopy' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatBlock.lua b/MY_Chat/src/MY_ChatBlock.lua index d23c98019..060938246 100644 --- a/MY_Chat/src/MY_ChatBlock.lua +++ b/MY_Chat/src/MY_ChatBlock.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatBlock' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatCopy.lua b/MY_Chat/src/MY_ChatCopy.lua index febed0240..9f7298624 100644 --- a/MY_Chat/src/MY_ChatCopy.lua +++ b/MY_Chat/src/MY_ChatCopy.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatCopy' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatEmotion.lua b/MY_Chat/src/MY_ChatEmotion.lua index 6f335a4e5..e0cbdfaf9 100644 --- a/MY_Chat/src/MY_ChatEmotion.lua +++ b/MY_Chat/src/MY_ChatEmotion.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatEmotion' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatFilter.lua b/MY_Chat/src/MY_ChatFilter.lua index 0fc43c341..746f90a17 100644 --- a/MY_Chat/src/MY_ChatFilter.lua +++ b/MY_Chat/src/MY_ChatFilter.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatFilter' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatMonitor.lua b/MY_Chat/src/MY_ChatMonitor.lua index 4a2e9ecdf..8f7b896b8 100644 --- a/MY_Chat/src/MY_ChatMonitor.lua +++ b/MY_Chat/src/MY_ChatMonitor.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatMonitor' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatMosaics.lua b/MY_Chat/src/MY_ChatMosaics.lua index 327d34e97..0cf9d3b3b 100644 --- a/MY_Chat/src/MY_ChatMosaics.lua +++ b/MY_Chat/src/MY_ChatMosaics.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatMosaics' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_ChatSwitch.lua b/MY_Chat/src/MY_ChatSwitch.lua index cabe2cf3b..14cb2604a 100644 --- a/MY_Chat/src/MY_ChatSwitch.lua +++ b/MY_Chat/src/MY_ChatSwitch.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_ChatSwitch' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_TalkEx.lua b/MY_Chat/src/MY_TalkEx.lua index 63f5dcb5c..70c352d59 100644 --- a/MY_Chat/src/MY_TalkEx.lua +++ b/MY_Chat/src/MY_TalkEx.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_TalkEx' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_TeamBalloon.lua b/MY_Chat/src/MY_TeamBalloon.lua index baa8b7239..8b4c742f8 100644 --- a/MY_Chat/src/MY_TeamBalloon.lua +++ b/MY_Chat/src/MY_TeamBalloon.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_TeamBalloon' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/MY_WhisperMention.lua b/MY_Chat/src/MY_WhisperMention.lua index d72d3d504..2415dfdb8 100644 --- a/MY_Chat/src/MY_WhisperMention.lua +++ b/MY_Chat/src/MY_WhisperMention.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_Chat' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Chat/src/PS.lua b/MY_Chat/src/PS.lua index 58aee1905..447de7065 100644 --- a/MY_Chat/src/PS.lua +++ b/MY_Chat/src/PS.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_Chat' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_Font/src/MY_Font.lua b/MY_Font/src/MY_Font.lua index 9d6dada04..8059c06b5 100644 --- a/MY_Font/src/MY_Font.lua +++ b/MY_Font/src/MY_Font.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_Font' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --[[#DEBUG BEGIN]]X.ReportModuleLoading(MODULE_PATH, 'START')--[[#DEBUG END]] diff --git a/MY_FontResource/src/MY_FontResource.lua b/MY_FontResource/src/MY_FontResource.lua index 1ccba263d..5808f640c 100644 --- a/MY_FontResource/src/MY_FontResource.lua +++ b/MY_FontResource/src/MY_FontResource.lua @@ -14,7 +14,7 @@ local PLUGIN_ROOT = X.PACKET_INFO.ROOT .. PLUGIN_NAME local MODULE_NAME = 'MY_FontResource' local _L = X.LoadLangPack(PLUGIN_ROOT .. '/lang/') -------------------------------------------------------------------------- -if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '>=3.0.0') then +if not X.AssertVersion(MODULE_NAME, _L[MODULE_NAME], '^29.0.2') then return end --------------------------------------------------------------------------