2025-04-28 00:59:03 +08:00

229 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()