From dc907581b319c3f58da6058c6cda094be4f25ff4 Mon Sep 17 00:00:00 2001 From: xingluo Date: Mon, 28 Apr 2025 00:59:03 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uuid.py | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 uuid.py diff --git a/uuid.py b/uuid.py new file mode 100644 index 0000000..9e5e9c7 --- /dev/null +++ b/uuid.py @@ -0,0 +1,229 @@ +import os +import json +import re +import argparse +from uuid import UUID + +def is_valid_uuid(uuid_str): + """验证UUID格式有效性""" + try: + UUID(uuid_str) + return True + except ValueError: + return False + +def get_uuid_variants(uuid_str): + """生成UUID的两种形式(带连字符和不带)""" + if not is_valid_uuid(uuid_str): + return [uuid_str] + + uuid_obj = UUID(uuid_str) + return [str(uuid_obj), uuid_obj.hex] + +def replace_uuid_variants(content, old_variants, new_variants): + """替换所有UUID变体""" + total = 0 + for old, new in zip(old_variants, new_variants): + pattern = re.compile(r'\b' + re.escape(old) + r'\b') + content, count = pattern.subn(new, content) + total += count + return content, total + +def process_snbt_file(file_path, old_uuid, new_uuid): + """处理SNBT文件""" + old_forms = get_uuid_variants(old_uuid) + new_forms = get_uuid_variants(new_uuid) + + try: + with open(file_path, 'r+', encoding='utf-8') as f: + content = f.read() + new_content, count = replace_uuid_variants(content, old_forms, new_forms) + + if count > 0: + print(f"在 {os.path.basename(file_path)} 中替换了 {count} 处UUID") + f.seek(0) + f.write(new_content) + f.truncate() + else: + print(f"在 {os.path.basename(file_path)} 中未找到目标UUID") + except Exception as e: + print(f"处理 {file_path} 时出错: {str(e)}") + +def replace_json_uuid(file_path, old_uuid, new_uuid): + """处理JSON文件""" + try: + with open(file_path, 'r+', encoding='utf-8') as f: + data = json.load(f) + changes = 0 + + if isinstance(data, list): + for item in data: + if item.get('uuid') == old_uuid: + item['uuid'] = new_uuid + changes += 1 + elif isinstance(data, dict): + if old_uuid in data: + data[new_uuid] = data.pop(old_uuid) + changes += 1 + + if changes > 0: + print(f"在 {os.path.basename(file_path)} 中替换了 {changes} 处UUID") + f.seek(0) + f.truncate() + json.dump(data, f, indent=4, ensure_ascii=False) + else: + print(f"在 {os.path.basename(file_path)} 中未找到目标UUID") + except Exception as e: + print(f"处理 {file_path} 时出错: {str(e)}") + +def rename_uuid_files(root_dir, old_uuid, new_uuid): + """重命名文件和目录""" + renamed_count = 0 + + for root, dirs, files in os.walk(root_dir, topdown=False): + # 先处理文件 + for name in files: + if old_uuid in name: + new_name = name.replace(old_uuid, new_uuid) + src = os.path.join(root, name) + dst = os.path.join(root, new_name) + try: + os.rename(src, dst) + renamed_count += 1 + print(f"已重命名文件:{name} → {new_name}") + except Exception as e: + print(f"文件重命名失败 [{src}]: {str(e)}") + + # 处理目录 + for name in dirs: + if old_uuid in name: + new_name = name.replace(old_uuid, new_uuid) + src = os.path.join(root, name) + dst = os.path.join(root, new_name) + try: + os.rename(src, dst) + print(f"已重命名目录:{name} → {new_name}") + except Exception as e: + print(f"目录重命名失败 [{src}]: {str(e)}") + + print(f"完成重命名操作,共处理 {renamed_count} 个文件") + +def process_directory(base_path, sub_path, old_uuid, new_uuid): + """处理指定目录下的所有相关文件""" + target_dir = os.path.join(base_path, "world", sub_path) + if not os.path.exists(target_dir): + print(f"未找到目录:{target_dir}") + return + + for root, _, files in os.walk(target_dir): + for file in files: + if file.endswith((".json", ".snbt")): + process_snbt_file(os.path.join(root, file), old_uuid, new_uuid) + +def process_server(server_path, old_uuid, new_uuid): + """主处理流程""" + # 处理核心JSON文件 + for json_file in ["usercache.json", "usernamecache.json"]: + file_path = os.path.join(server_path, json_file) + if os.path.exists(file_path): + replace_json_uuid(file_path, old_uuid, new_uuid) + + # 处理各模块数据 + modules = [ + ("ftbteams/party", "FTB Teams队伍数据"), + ("ftbteams/player", "FTB Teams玩家数据"), + ("ftbchunks", "FTB Chunks数据"), + ("ftbquests", "FTB Quests数据") + ] + + for path, name in modules: + print(f"\n正在处理{name}...") + process_directory(server_path, path, old_uuid, new_uuid) + + # 处理world目录文件重命名 + world_dir = os.path.join(server_path, "world") + if os.path.exists(world_dir): + print("\n正在处理文件重命名...") + rename_uuid_files(world_dir, old_uuid, new_uuid) + else: + print(f"未找到world目录:{world_dir}") + +def load_config(config_path): + """加载配置文件""" + try: + with open(config_path, 'r', encoding='utf-8') as f: + config = json.load(f) + + if not all(k in config for k in ("path", "mappings")): + raise ValueError("配置文件缺少必要字段") + + if not isinstance(config["mappings"], dict): + raise TypeError("mappings字段必须为字典") + + return config + except Exception as e: + print(f"配置文件错误: {str(e)}") + exit(1) + +def main(): + parser = argparse.ArgumentParser(description="Minecraft UUID迁移工具") + parser.add_argument("--config", help="配置文件路径") + args = parser.parse_args() + + print("=" * 50) + print("Minecraft UUID迁移工具 v4.0") + print("=" * 50) + + if args.config: + if not os.path.exists(args.config): + print(f"错误:配置文件 {args.config} 不存在") + exit(1) + + config = load_config(args.config) + server_path = config["path"] + + if not os.path.isdir(server_path): + print(f"错误:无效的服务端路径 {server_path}") + exit(1) + + print(f"\n开始批量处理 {server_path}") + for old_uuid, new_uuid in config["mappings"].items(): + print(f"\n{'='*30} 正在处理 {old_uuid[:8]}... {'='*30}") + process_server(server_path, old_uuid, new_uuid) + exit(0) + + # 交互模式 + while True: + server_path = input("\n请输入服务端路径(输入q退出): ").strip() + if server_path.lower() == "q": + break + + if not os.path.isdir(server_path): + print("错误:路径无效或不存在") + continue + + while True: + print("\n" + "=" * 50) + old_uuid = input("请输入原UUID(输入q返回上级): ").strip() + if old_uuid.lower() == "q": + break + + if not is_valid_uuid(old_uuid): + print("错误:无效的UUID格式") + continue + + new_uuid = input("请输入新UUID: ").strip() + if not is_valid_uuid(new_uuid): + print("错误:无效的新UUID格式") + continue + + process_server(server_path, old_uuid, new_uuid) + + choice = input("\n是否继续处理其他UUID?(y/n): ").lower() + if choice != "y": + break + + print("\n感谢使用!") + +if __name__ == "__main__": + main() \ No newline at end of file