From 4bcb45d6833d7cc86fdd8c5f024b85a2259b5130 Mon Sep 17 00:00:00 2001 From: "ProjectKoi-Kalo\\Kalo" Date: Tue, 25 Nov 2025 14:10:22 +0800 Subject: [PATCH] zc --- .kilocode/mcp.json | 22 ------ .kilocode/rules/rimworld.md | 4 +- MCP/mcpserver_stdio_complete.py | 118 ++++++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 54 deletions(-) delete mode 100644 .kilocode/mcp.json diff --git a/.kilocode/mcp.json b/.kilocode/mcp.json deleted file mode 100644 index 266cf16a..00000000 --- a/.kilocode/mcp.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "mcpServers": { - "rimworld-knowledge-base": { - "command": "python", - "args": [ - "mcpserver_stdio.py" - ], - "cwd": "${workspaceFolder}/MCP/", - "disabled": false, - "alwaysAllow": [] - }, - "rimworld-knowledge-base-proxy": { - "command": "python", - "args": [ - "mcpserver_stdio_simple_proxy.py" - ], - "cwd": "${workspaceFolder}/MCP/", - "disabled": true, - "alwaysAllow": [] - } - } -} \ No newline at end of file diff --git a/.kilocode/rules/rimworld.md b/.kilocode/rules/rimworld.md index 11395210..9af60e7f 100644 --- a/.kilocode/rules/rimworld.md +++ b/.kilocode/rules/rimworld.md @@ -4,12 +4,12 @@ You are an expert assistant for developing mods for the game RimWorld 1.6. Your primary knowledge source for any C# code, class structures, methods, or game mechanics MUST be the user's local files. Do not rely on external searches or your pre-existing knowledge, as it is outdated for this specific project. ## Tool Usage Mandate -When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-knowledge-base` tool to retrieve relevant context from the local knowledge base. +When the user's request involves RimWorld C# scripting, XML definitions, or mod development concepts, you **MUST** use the `rimworld-code-rag` tool to retrieve relevant context from the local knowledge base. ## Key File Paths Always remember these critical paths for your work: -- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\Data\dll1.6` (This contains the decompiled game source code as .txt files). +- **Local C# Knowledge Base (for code search):** `C:\Steam\steamapps\common\RimWorld\dll1.6` (This contains the decompiled game source code as .txt files). - **User's Mod Project (for editing):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226` - **User's C# Project (for building):** `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire` diff --git a/MCP/mcpserver_stdio_complete.py b/MCP/mcpserver_stdio_complete.py index 035c1768..45d43baf 100644 --- a/MCP/mcpserver_stdio_complete.py +++ b/MCP/mcpserver_stdio_complete.py @@ -54,6 +54,7 @@ class SymbolIndex: """ def __init__(self): self.symbol_map = {} # symbol_id -> file_path + self.translation_map = defaultdict(list) # translation_text -> [symbol_id] self.files_cache = [] # 所有文件路径列表 self.is_initialized = False self._lock = threading.Lock() @@ -77,19 +78,26 @@ class SymbolIndex: else: logging.warning(f"Source root not found: {RIMWORLD_SOURCE_ROOT}") - # 2. 扫描 XML Defs - # 扫描 Data 目录下的所有子目录 + # 2. 扫描 XML Defs 和 语言文件 if os.path.exists(RIMWORLD_DATA_ROOT): for root, _, files in os.walk(RIMWORLD_DATA_ROOT): for file in files: if file.endswith('.xml'): full_path = os.path.join(root, file) self.files_cache.append(full_path) - # 快速解析 XML 找 defName - try: - self._scan_xml_defs(full_path) - except Exception as e: - logging.warning(f"解析 XML 失败 {full_path}: {e}") + + # 判断是 Def 定义还是翻译文件 + # 简单判断:路径包含 "Languages" 且包含 "DefInjected" + if "Languages" in full_path and "DefInjected" in full_path: + try: + self._scan_translations(full_path) + except Exception as e: + logging.warning(f"解析翻译失败 {full_path}: {e}") + else: + try: + self._scan_xml_defs(full_path) + except Exception as e: + logging.warning(f"解析 XML 失败 {full_path}: {e}") else: logging.warning(f"Data root not found: {RIMWORLD_DATA_ROOT}") @@ -105,18 +113,51 @@ class SymbolIndex: for name in def_names: self.symbol_map[f"xml:{name}"] = path + def _scan_translations(self, path): + content = read_file_content(path) + if not content: return + + # 提取 Translation + # 示例: 突击步枪 + matches = re.findall(r'<([\w\.]+)>([^<]+)= 3: # e.g. ThingDef.Gun_AssaultRifle.label + def_name = parts[1] + + symbol_id = f"xml:{def_name}" + self.translation_map[text.strip()].append(symbol_id) + def search_symbols(self, keyword: str, kind: str = None) -> List[Tuple[str, str]]: - """简单的关键词匹配""" + """关键词匹配,支持翻译反查""" results = [] kw_lower = keyword.lower() + + # 1. 搜索翻译索引 + found_via_translation = set() + for trans_text, symbols in self.translation_map.items(): + if kw_lower in trans_text.lower(): + for sym in symbols: + if sym in self.symbol_map: # 确保该 Def 确实存在 + found_via_translation.add(sym) + results.append((sym, self.symbol_map[sym])) + + # 2. 搜索原始符号 for sym, path in self.symbol_map.items(): + if sym in found_via_translation: continue # 避免重复 + if kind == 'csharp' and sym.startswith('xml:'): continue if kind == 'xml' and not sym.startswith('xml:'): continue if kw_lower in sym.lower(): results.append((sym, path)) - # 移除硬限制,以便后续排序能找到最佳结果 - # if len(results) > 200: break + return results # 全局索引实例 @@ -126,7 +167,8 @@ global_index = SymbolIndex() def read_file_content(path: str) -> str: """健壮的文件读取""" - encodings = ['utf-8', 'utf-8-sig', 'gbk', 'latin-1'] + # 优先尝试 utf-8-sig 以去除 BOM + encodings = ['utf-8-sig', 'utf-8', 'gbk', 'latin-1'] for enc in encodings: try: with open(path, 'r', encoding=enc) as f: @@ -141,28 +183,44 @@ def extract_xml_fragment(file_path: str, def_name: str) -> str: """提取 XML 中特定的 Def 块""" content = read_file_content(file_path) try: - # 尝试正则匹配整个 Def 块 - # 假设 Def 格式为 ...NAME... - - # 策略 1: 查找 NAME,然后向上找最近的 - pattern = r"<(\w+)\s*(?:Name=\"[^\"]*\")?>\s*" + re.escape(def_name) + r"" + # 策略:先找到 NAME,然后向后搜索最近的父级标签 + pattern = r"" + re.escape(def_name) + r"" match = re.search(pattern, content) - if not match: - # 策略 2: 查找 defName="NAME" - pattern = r"<(\w+)\s+[^>]*defName=\"" + re.escape(def_name) + r"\"" - match = re.search(pattern, content) - if match: - tag_name = match.group(1) - # 找到开始位置 - start_pos = match.start() - # 寻找对应的结束标签 - # 这是一个简化的查找,不支持嵌套同名标签,但在 RimWorld Defs 中通常足够 - end_tag = f"" - end_pos = content.find(end_tag, start_pos) - if end_pos != -1: - return content[start_pos:end_pos + len(end_tag)] + def_start = match.start() + def_end = match.end() + + # 向前搜索最近的开始标签 = 0: + lt_pos = content.rfind('<', 0, cursor) + if lt_pos == -1: break + + # 跳过结束标签 + close_tag = f"" + close_pos = content.find(close_tag, def_end) + + if close_pos != -1: + return content[lt_pos : close_pos + len(close_tag)] + + # 如果找到了开始标签但没匹配上(逻辑上不应该发生,除非XML结构很怪),停止搜索 + break + except Exception as e: logging.error(f"Error extracting XML fragment: {e}")