From 3cda3c2238721273e7d11e71e4bd12ab0e8a2813 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 16 Oct 2025 17:33:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E7=9A=84ip:port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor-app/src-tauri/src/profiler_ws.rs | 14 ++++-- packages/editor-app/src/App.tsx | 4 +- .../src/components/ConsolePanel.tsx | 19 +++++++- .../src/components/SceneHierarchy.tsx | 46 +++++++++++++++++-- .../src/services/ProfilerService.ts | 6 ++- .../editor-app/src/styles/ConsolePanel.css | 36 ++++++++++++--- .../editor-app/src/styles/SceneHierarchy.css | 44 ++++++++++++++++++ .../editor-core/src/Services/LogService.ts | 6 ++- 8 files changed, 156 insertions(+), 19 deletions(-) diff --git a/packages/editor-app/src-tauri/src/profiler_ws.rs b/packages/editor-app/src-tauri/src/profiler_ws.rs index 0bfa0c0c..92dcc4e3 100644 --- a/packages/editor-app/src-tauri/src/profiler_ws.rs +++ b/packages/editor-app/src-tauri/src/profiler_ws.rs @@ -132,11 +132,13 @@ async fn handle_connection( match msg { Ok(Message::Text(text)) => { // Parse incoming messages - if let Ok(json_value) = serde_json::from_str::(&text) { - if json_value.get("type").and_then(|t| t.as_str()) == Some("debug_data") { + if let Ok(mut json_value) = serde_json::from_str::(&text) { + let msg_type = json_value.get("type").and_then(|t| t.as_str()); + + if msg_type == Some("debug_data") { // Broadcast debug data from game client to all clients (including frontend) tx.send(text).ok(); - } else if json_value.get("type").and_then(|t| t.as_str()) == Some("ping") { + } else if msg_type == Some("ping") { // Respond to ping let _ = tx.send( serde_json::json!({ @@ -145,6 +147,12 @@ async fn handle_connection( }) .to_string(), ); + } else if msg_type == Some("log") { + // Inject clientId into log messages + if let Some(data) = json_value.get_mut("data").and_then(|d| d.as_object_mut()) { + data.insert("clientId".to_string(), serde_json::Value::String(peer_addr.to_string())); + } + tx.send(json_value.to_string()).ok(); } else { // Forward all other messages (like get_raw_entity_list, get_entity_details, etc.) // to all connected clients (this enables frontend -> game client communication) diff --git a/packages/editor-app/src/App.tsx b/packages/editor-app/src/App.tsx index befa54a7..a5eb9193 100644 --- a/packages/editor-app/src/App.tsx +++ b/packages/editor-app/src/App.tsx @@ -148,8 +148,8 @@ function App() { // 监听远程日志事件 window.addEventListener('profiler:remote-log', ((event: CustomEvent) => { - const { level, message, timestamp } = event.detail; - logService.addRemoteLog(level, message, timestamp); + const { level, message, timestamp, clientId } = event.detail; + logService.addRemoteLog(level, message, timestamp, clientId); }) as EventListener); Core.services.registerInstance(UIRegistry, uiRegistry); diff --git a/packages/editor-app/src/components/ConsolePanel.tsx b/packages/editor-app/src/components/ConsolePanel.tsx index 396dbc36..94fa81a8 100644 --- a/packages/editor-app/src/components/ConsolePanel.tsx +++ b/packages/editor-app/src/components/ConsolePanel.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useRef } from 'react'; import { LogService, LogEntry } from '@esengine/editor-core'; import { LogLevel } from '@esengine/ecs-framework'; -import { Trash2, AlertCircle, Info, AlertTriangle, XCircle, Bug, Search, Maximize2, ChevronRight, ChevronDown } from 'lucide-react'; +import { Trash2, AlertCircle, Info, AlertTriangle, XCircle, Bug, Search, Maximize2, ChevronRight, ChevronDown, Wifi } from 'lucide-react'; import { JsonViewer } from './JsonViewer'; import '../styles/ConsolePanel.css'; @@ -19,6 +19,7 @@ export function ConsolePanel({ logService }: ConsolePanelProps) { LogLevel.Error, LogLevel.Fatal ])); + const [showRemoteOnly, setShowRemoteOnly] = useState(false); const [autoScroll, setAutoScroll] = useState(true); const [expandedLogs, setExpandedLogs] = useState>(new Set()); const [jsonViewerData, setJsonViewerData] = useState(null); @@ -65,6 +66,7 @@ export function ConsolePanel({ logService }: ConsolePanelProps) { const filteredLogs = logs.filter(log => { if (!levelFilter.has(log.level)) return false; + if (showRemoteOnly && log.source !== 'remote') return false; if (filter && !log.message.toLowerCase().includes(filter.toLowerCase())) { return false; } @@ -224,6 +226,8 @@ export function ConsolePanel({ logService }: ConsolePanelProps) { [LogLevel.Error]: logs.filter(l => l.level === LogLevel.Error || l.level === LogLevel.Fatal).length }; + const remoteLogCount = logs.filter(l => l.source === 'remote').length; + return (
@@ -246,6 +250,14 @@ export function ConsolePanel({ logService }: ConsolePanelProps) {
+
)} +
+ + setSearchQuery(e.target.value)} + /> +
{displayEntities.length === 0 ? (
@@ -134,7 +169,7 @@ export function SceneHierarchy({ entityStore, messageHub }: SceneHierarchyProps)
) : isRemoteConnected ? (
    - {remoteEntities.map(entity => ( + {displayEntities.map(entity => (
  • {entity.name} + {entity.tag !== 0 && ( + + #{entity.tag} + + )} {entity.componentCount > 0 && ( {entity.componentCount} )} diff --git a/packages/editor-app/src/services/ProfilerService.ts b/packages/editor-app/src/services/ProfilerService.ts index ef5790d0..2772d5ee 100644 --- a/packages/editor-app/src/services/ProfilerService.ts +++ b/packages/editor-app/src/services/ProfilerService.ts @@ -64,6 +64,7 @@ export class ProfilerService { private currentData: ProfilerData | null = null; private checkServerInterval: NodeJS.Timeout | null = null; private reconnectTimeout: NodeJS.Timeout | null = null; + private clientIdMap: Map = new Map(); // 客户端地址 -> 客户端ID映射 constructor() { const settings = SettingsService.getInstance(); @@ -370,11 +371,14 @@ export class ProfilerService { } } + const clientId = data.clientId || data.client_id || 'unknown'; + window.dispatchEvent(new CustomEvent('profiler:remote-log', { detail: { level, message, - timestamp: data.timestamp ? new Date(data.timestamp) : new Date() + timestamp: data.timestamp ? new Date(data.timestamp) : new Date(), + clientId } })); } diff --git a/packages/editor-app/src/styles/ConsolePanel.css b/packages/editor-app/src/styles/ConsolePanel.css index 581307f7..c9a18d65 100644 --- a/packages/editor-app/src/styles/ConsolePanel.css +++ b/packages/editor-app/src/styles/ConsolePanel.css @@ -107,37 +107,46 @@ } .console-filter-btn:nth-child(1) { - color: #858585; + color: #10b981; } .console-filter-btn:nth-child(1).active { + color: #34d399; + border-color: #10b981; +} + +.console-filter-btn:nth-child(2) { + color: #858585; +} + +.console-filter-btn:nth-child(2).active { color: #a0a0a0; border-color: #858585; } -.console-filter-btn:nth-child(2) { +.console-filter-btn:nth-child(3) { color: #4a9eff; } -.console-filter-btn:nth-child(2).active { +.console-filter-btn:nth-child(3).active { color: #6eb3ff; border-color: #4a9eff; } -.console-filter-btn:nth-child(3) { +.console-filter-btn:nth-child(4) { color: #ffc107; } -.console-filter-btn:nth-child(3).active { +.console-filter-btn:nth-child(4).active { color: #ffd54f; border-color: #ffc107; } -.console-filter-btn:nth-child(4) { +.console-filter-btn:nth-child(5) { color: #f44336; } -.console-filter-btn:nth-child(4).active { +.console-filter-btn:nth-child(5).active { color: #ef5350; border-color: #f44336; } @@ -215,6 +224,19 @@ font-weight: 600; } +.log-entry-client { + color: #10b981; + font-size: 9px; + white-space: nowrap; + padding: 1px 6px; + flex-shrink: 0; + background: rgba(16, 185, 129, 0.15); + border: 1px solid rgba(16, 185, 129, 0.4); + border-radius: var(--radius-sm); + font-weight: 600; + font-family: var(--font-family-mono); +} + .log-entry-remote { border-left: 2px solid #4a9eff; background: rgba(74, 158, 255, 0.05); diff --git a/packages/editor-app/src/styles/SceneHierarchy.css b/packages/editor-app/src/styles/SceneHierarchy.css index 4a598457..60af5168 100644 --- a/packages/editor-app/src/styles/SceneHierarchy.css +++ b/packages/editor-app/src/styles/SceneHierarchy.css @@ -45,6 +45,35 @@ animation: pulse-green 1.5s ease-in-out infinite; } +.hierarchy-search { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: var(--spacing-sm) var(--spacing-md); + border-bottom: 1px solid var(--color-border-default); + background-color: var(--color-bg-base); + flex-shrink: 0; +} + +.hierarchy-search svg { + color: var(--color-text-tertiary); + flex-shrink: 0; +} + +.hierarchy-search input { + flex: 1; + background: transparent; + border: none; + outline: none; + color: var(--color-text-primary); + font-size: var(--font-size-sm); + padding: 0; +} + +.hierarchy-search input::placeholder { + color: var(--color-text-tertiary); +} + .hierarchy-content { flex: 1; overflow-y: auto; @@ -171,6 +200,21 @@ color: var(--color-text-tertiary); } +.entity-tag { + display: flex; + align-items: center; + justify-content: center; + padding: 2px 6px; + background-color: rgba(139, 92, 246, 0.15); + border: 1px solid rgba(139, 92, 246, 0.4); + border-radius: var(--radius-sm); + color: rgb(167, 139, 250); + font-size: 10px; + font-weight: var(--font-weight-medium); + font-family: var(--font-family-mono); + flex-shrink: 0; +} + .component-count { display: flex; align-items: center; diff --git a/packages/editor-core/src/Services/LogService.ts b/packages/editor-core/src/Services/LogService.ts index e0bc8385..262af5e2 100644 --- a/packages/editor-core/src/Services/LogService.ts +++ b/packages/editor-core/src/Services/LogService.ts @@ -8,6 +8,7 @@ export interface LogEntry { source: string; message: string; args: unknown[]; + clientId?: string; // 远程客户端ID } export type LogListener = (entry: LogEntry) => void; @@ -124,14 +125,15 @@ export class LogService implements IService { /** * 添加远程日志(从远程游戏接收) */ - public addRemoteLog(level: LogLevel, message: string, timestamp?: Date): void { + public addRemoteLog(level: LogLevel, message: string, timestamp?: Date, clientId?: string): void { const entry: LogEntry = { id: this.nextId++, timestamp: timestamp || new Date(), level, source: 'remote', message, - args: [] + args: [], + clientId }; this.logs.push(entry);