mcpywrap 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/PKG-INFO +1 -2
  2. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/AddonsPack.py +12 -6
  3. mcpywrap-0.2.2/mcpywrap/builders/MapPack.py +173 -0
  4. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/__init__.py +2 -2
  5. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/dependency_manager.py +70 -35
  6. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/file_merge.py +6 -0
  7. mcpywrap-0.2.2/mcpywrap/builders/project_builder.py +346 -0
  8. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/build_cmd.py +28 -22
  9. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/run_cmd.py +89 -103
  10. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/config.py +20 -3
  11. mcpywrap-0.2.2/mcpywrap/mcstudio/studio_server.py +719 -0
  12. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/studio_server_ui.py +280 -6
  13. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlinks.py +4 -4
  14. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/level_dat.py +318 -10
  15. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/ui/project_ui.py +234 -22
  16. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/PKG-INFO +1 -2
  17. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/SOURCES.txt +1 -0
  18. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/requires.txt +0 -1
  19. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/pyproject.toml +1 -2
  20. mcpywrap-0.2.0/mcpywrap/builders/project_builder.py +0 -195
  21. mcpywrap-0.2.0/mcpywrap/mcstudio/studio_server.py +0 -312
  22. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/README.md +0 -0
  23. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/__init__.py +0 -0
  24. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/__main__.py +0 -0
  25. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/builders/watcher.py +0 -0
  26. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/cli.py +0 -0
  27. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/__init__.py +0 -0
  28. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/add_cmd.py +0 -0
  29. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/default_cmd.py +0 -0
  30. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/dev_cmd.py +0 -0
  31. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/edit_cmd.py +0 -0
  32. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/init_cmd.py +0 -0
  33. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/mod_cmd.py +0 -0
  34. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/modsdk_cmd.py +0 -0
  35. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/publish_cmd.py +0 -0
  36. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/remove_cmd.py +0 -0
  37. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/commands/ui_cmd.py +0 -0
  38. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/custom_packaging.py +0 -0
  39. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/SimpleMonitor.py +0 -0
  40. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/__init__.py +0 -0
  41. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/editor.py +0 -0
  42. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/game.py +0 -0
  43. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/mcs.py +0 -0
  44. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/runtime_cppconfig.py +0 -0
  45. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlink_helper_global.py +0 -0
  46. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/mcstudio/symlink_helper_map.py +0 -0
  47. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/__init__.py +0 -0
  48. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/addons.py +0 -0
  49. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/map.py +0 -0
  50. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/netease_modsdk.py +0 -0
  51. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/template/generate_mod_files.py +0 -0
  52. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/minecraft/template/mod_template.py +0 -0
  53. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/ui/__init__.py +0 -0
  54. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/__init__.py +0 -0
  55. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/print_guide.py +0 -0
  56. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/project_setup.py +0 -0
  57. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/py3to2_util.py +0 -0
  58. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap/utils/utils.py +0 -0
  59. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/dependency_links.txt +0 -0
  60. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/entry_points.txt +0 -0
  61. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/mcpywrap.egg-info/top_level.txt +0 -0
  62. {mcpywrap-0.2.0 → mcpywrap-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpywrap
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A wrapper for Minecraft China Edition Addons management
5
5
  Author-email: boybook <boybook@easecation.net>
6
6
  License: MIT
@@ -20,7 +20,6 @@ Requires-Dist: tomli-w
20
20
  Requires-Dist: setuptools
21
21
  Requires-Dist: PyQt5>=5.15.0
22
22
  Requires-Dist: psutil>=5.8.0
23
- Requires-Dist: amulet-nbt>=2.1.4
24
23
  Requires-Dist: rich>=10.0.0
25
24
 
26
25
  # 🧰 mcpywrap
@@ -78,10 +78,13 @@ class AddonsPack(object):
78
78
  return True
79
79
  return False
80
80
 
81
- def copy_behavior_to(self, target_dir: str):
82
- """复制行为包和资源包到目标目录"""
81
+ def copy_behavior_to(self, target_dir: str, rename: str = None):
82
+ """
83
+ 复制行为包和资源包到目标目录
84
+ target_dir为父目录
85
+ """
83
86
  if self.behavior_pack_dir:
84
- target_path = os.path.join(target_dir, os.path.basename(self.behavior_pack_dir))
87
+ target_path = os.path.join(target_dir, os.path.basename(self.behavior_pack_dir) if not rename else rename)
85
88
  os.makedirs(target_path, exist_ok=True)
86
89
 
87
90
  # 使用自定义复制函数而不是shutil.copytree
@@ -106,10 +109,13 @@ class AddonsPack(object):
106
109
  else:
107
110
  shutil.copy2(src_file, dest_file)
108
111
 
109
- def copy_resource_to(self, target_dir: str):
110
- """复制资源包到目标目录"""
112
+ def copy_resource_to(self, target_dir: str, rename: str = None):
113
+ """
114
+ 复制资源包到目标目录
115
+ target_dir为父目录
116
+ """
111
117
  if self.resource_pack_dir:
112
- target_path = os.path.join(target_dir, os.path.basename(self.resource_pack_dir))
118
+ target_path = os.path.join(target_dir, os.path.basename(self.resource_pack_dir) if not rename else rename)
113
119
  os.makedirs(target_path, exist_ok=True)
114
120
 
115
121
  # 使用自定义复制函数而不是shutil.copytree
@@ -0,0 +1,173 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import os
4
+ import shutil
5
+ import click
6
+ import json
7
+
8
+ from ..mcstudio.symlinks import setup_map_packs_symlinks
9
+
10
+ class MapPack(object):
11
+
12
+ pkg_name: str
13
+ path: str
14
+
15
+ behavior_packs_dir: str
16
+ resource_packs_dir: str
17
+
18
+ behavior_packs = []
19
+ resource_packs = []
20
+
21
+ def __init__(self, pkg_name: str, path: str):
22
+ self.pkg_name = pkg_name
23
+ self.path = path
24
+
25
+ # 读取配置文件
26
+ self.behavior_packs_dir = os.path.join(self.path, "behavior_packs")
27
+ self.resource_packs_dir = os.path.join(self.path, "resource_packs")
28
+
29
+ if not os.path.exists(self.behavior_packs_dir):
30
+ os.makedirs(self.behavior_packs_dir)
31
+
32
+ # 遍历行为包目录
33
+ for pack in os.listdir(self.behavior_packs_dir):
34
+ pack_path = os.path.join(self.behavior_packs_dir, pack)
35
+ if os.path.isdir(pack_path):
36
+ self.behavior_packs.append(pack_path)
37
+
38
+ if not os.path.exists(self.resource_packs_dir):
39
+ os.makedirs(self.resource_packs_dir)
40
+
41
+ # 遍历资源包目录
42
+ for pack in os.listdir(self.resource_packs_dir):
43
+ pack_path = os.path.join(self.resource_packs_dir, pack)
44
+ if os.path.isdir(pack_path):
45
+ self.resource_packs.append(pack_path)
46
+
47
+ def copy_level_data_to(self, target_dir: str):
48
+ """
49
+ 复制地图数据到目标目录
50
+ """
51
+ # level.dat
52
+ level_dat_path = os.path.join(target_dir, "level.dat")
53
+ if not os.path.exists(level_dat_path):
54
+ origin_level_dat_path = os.path.join(self.path, "level.dat")
55
+ if os.path.exists(origin_level_dat_path):
56
+ shutil.copy2(origin_level_dat_path, level_dat_path)
57
+ # levelname.txt
58
+ levelname_txt_path = os.path.join(target_dir, "levelname.txt")
59
+ if not os.path.exists(levelname_txt_path):
60
+ origin_levelname_txt_path = os.path.join(self.path, "levelname.txt")
61
+ if os.path.exists(origin_levelname_txt_path):
62
+ shutil.copy2(origin_levelname_txt_path, levelname_txt_path)
63
+ # db
64
+ level_db_dir = os.path.join(target_dir, "db")
65
+ if not os.path.exists(level_db_dir) and os.path.exists(os.path.join(self.path, "db")):
66
+ shutil.copytree(os.path.join(self.path, "db"), level_db_dir)
67
+
68
+ def copy_resource_packs_to(self, target_map_dir: str):
69
+ """
70
+ 复制资源包到目标目录
71
+ """
72
+ for pack in self.resource_packs:
73
+ pack_name = os.path.basename(pack)
74
+ target_pack_dir = os.path.join(target_map_dir, "resource_packs", pack_name)
75
+ # 先删除目标目录下的包
76
+ if os.path.exists(target_pack_dir):
77
+ shutil.rmtree(target_pack_dir)
78
+ shutil.copytree(pack, target_pack_dir)
79
+
80
+ def copy_behavior_packs_to(self, target_map_dir: str):
81
+ """
82
+ 复制行为包到目标目录
83
+ """
84
+ for pack in self.behavior_packs:
85
+ pack_name = os.path.basename(pack)
86
+ target_pack_dir = os.path.join(target_map_dir, "behavior_packs", pack_name)
87
+ # 先删除目标目录下的包
88
+ if os.path.exists(target_pack_dir):
89
+ shutil.rmtree(target_pack_dir)
90
+ shutil.copytree(pack, target_pack_dir)
91
+
92
+
93
+ def setup_packs_symlinks_to(self, level_id: str, target_dir: str):
94
+ """
95
+ 设置行为包和资源包的符号链接到目标目录(本体到远程)
96
+ """
97
+ setup_map_packs_symlinks(self.path, level_id, target_dir)
98
+
99
+ def setup_world_packs_config(self):
100
+ """根据当前path,生成世界需加载的pack的配置文件"""
101
+ behavior_packs_config = []
102
+ if os.path.exists(self.behavior_packs_dir):
103
+ behavior_packs_config = _find_and_extract_pack_info(self.behavior_packs_dir)
104
+ if behavior_packs_config:
105
+ world_behavior_packs_path = os.path.join(self.path, "world_behavior_packs.json")
106
+ with open(world_behavior_packs_path, 'w', encoding='utf-8') as f:
107
+ json.dump(behavior_packs_config, f, ensure_ascii=False, indent=4)
108
+
109
+ resource_packs_config = []
110
+ if os.path.exists(self.resource_packs_dir):
111
+ resource_packs_config = _find_and_extract_pack_info(self.resource_packs_dir)
112
+ if resource_packs_config:
113
+ world_resource_packs_path = os.path.join(self.path, "world_resource_packs.json")
114
+ with open(world_resource_packs_path, 'w', encoding='utf-8') as f:
115
+ json.dump(resource_packs_config, f, ensure_ascii=False, indent=4)
116
+
117
+ return behavior_packs_config, resource_packs_config
118
+
119
+ def _find_and_extract_pack_info(packs_dir):
120
+ """
121
+ 搜索指定目录中的所有包,并从manifest.json中提取信息
122
+
123
+ Args:
124
+ packs_dir: 包目录路径
125
+
126
+ Returns:
127
+ list: 包配置列表
128
+ """
129
+ packs_config = []
130
+
131
+ # 遍历目录下的所有子目录
132
+ for pack_name in os.listdir(packs_dir):
133
+ pack_path = os.path.join(packs_dir, pack_name)
134
+
135
+ # 只处理目录
136
+ if not os.path.isdir(pack_path):
137
+ continue
138
+
139
+ # 查找manifest.json或pack_manifest.json
140
+ manifest_path = os.path.join(pack_path, "manifest.json")
141
+ if not os.path.exists(manifest_path):
142
+ manifest_path = os.path.join(pack_path, "pack_manifest.json")
143
+ if not os.path.exists(manifest_path):
144
+ continue
145
+
146
+ try:
147
+ with open(manifest_path, 'r', encoding='utf-8') as f:
148
+ manifest = json.load(f)
149
+
150
+ # 提取UUID和版本信息
151
+ if 'header' in manifest:
152
+ pack_id = manifest['header'].get('uuid')
153
+ version = manifest['header'].get('version', [0, 0, 1])
154
+
155
+ # 确保版本是列表格式
156
+ if isinstance(version, list):
157
+ version_array = version
158
+ elif isinstance(version, dict) and 'major' in version and 'minor' in version and 'patch' in version:
159
+ version_array = [version['major'], version['minor'], version['patch']]
160
+ else:
161
+ version_array = [0, 0, 1] # 默认版本
162
+
163
+ if pack_id:
164
+ pack_config = {
165
+ "pack_id": pack_id,
166
+ "type": "Addon",
167
+ "version": version_array
168
+ }
169
+ packs_config.append(pack_config)
170
+ except Exception as e:
171
+ click.echo(f"⚠️ 读取包配置失败: {pack_name} - {str(e)}", style="yellow")
172
+
173
+ return packs_config
@@ -3,8 +3,8 @@
3
3
  构建模块包,提供项目构建、文件处理和监控功能
4
4
  """
5
5
 
6
- from .project_builder import ProjectBuilder
6
+ from .project_builder import AddonProjectBuilder
7
7
  from .watcher import ProjectWatcher, FileWatcher
8
8
  from .dependency_manager import DependencyManager
9
9
 
10
- __all__ = ['ProjectBuilder', 'ProjectWatcher', 'FileWatcher', 'DependencyManager']
10
+ __all__ = ['AddonProjectBuilder', 'ProjectWatcher', 'FileWatcher', 'DependencyManager']
@@ -11,13 +11,14 @@ from pathlib import Path
11
11
  from typing import Dict, List, Optional, Set
12
12
  import click
13
13
 
14
+ from ..config import CONFIG_FILE, check_has_mcpywrap_config
14
15
  from .AddonsPack import AddonsPack
15
16
 
16
17
  class DependencyNode:
17
18
  """依赖树节点"""
18
19
  def __init__(self, name: str, addon_pack: AddonsPack, parent=None):
19
20
  self.name = name
20
- self.addon_pack = addon_pack
21
+ self.addon_pack: AddonsPack = addon_pack
21
22
  self.parent = parent
22
23
  self.children: List[DependencyNode] = []
23
24
 
@@ -30,6 +31,39 @@ class DependencyNode:
30
31
 
31
32
  def __repr__(self):
32
33
  return self.__str__()
34
+
35
+
36
+ def _decode_direct_url(direct_url_path: str) -> Optional[str]:
37
+ with open(direct_url_path, 'r', encoding='utf-8') as f:
38
+ try:
39
+ direct_url = json.load(f)
40
+ # 读取其中的url
41
+ if "url" in direct_url:
42
+ url = direct_url["url"]
43
+ # 处理file://开头的路径
44
+ if url.startswith("file:///"):
45
+ # 移除file:/// 前缀
46
+ if sys.platform == "win32":
47
+ # Windows 路径处理 (例如 file:///D:/path)
48
+ url = url[8:] # 去除 file:///
49
+ else:
50
+ url = "/" + url[8:] # 保留根目录斜杠
51
+ url = os.path.abspath(url)
52
+ # 兼容处理旧格式 file://
53
+ elif url.startswith("file://"):
54
+ url = url[7:]
55
+ # 对URL进行解码,处理%编码的特殊字符
56
+ from urllib.parse import unquote
57
+ url = unquote(url)
58
+ url = os.path.abspath(url)
59
+
60
+ # 确保路径格式一致
61
+ if sys.platform == "win32":
62
+ url = url.replace("\\", "/")
63
+
64
+ return url
65
+ except json.JSONDecodeError:
66
+ logging.warning(f"无法解析 {direct_url_path} 的JSON内容")
33
67
 
34
68
  class DependencyManager:
35
69
  """依赖管理器"""
@@ -37,7 +71,7 @@ class DependencyManager:
37
71
  self.dependency_map: Dict[str, AddonsPack] = {}
38
72
  self.root_node: Optional[DependencyNode] = None
39
73
  self.processed_deps: Set[str] = set()
40
-
74
+
41
75
  def find_dependency_path(self, package_name: str) -> Optional[str]:
42
76
  """
43
77
  查找依赖包的真实路径,支持常规安装和pip install -e (编辑安装)
@@ -62,39 +96,13 @@ class DependencyManager:
62
96
  # 处理direct_url.json获取包路径
63
97
  direct_url_path = dist_info / "direct_url.json"
64
98
  if direct_url_path.exists():
65
- with open(direct_url_path, 'r', encoding='utf-8') as f:
66
- try:
67
- direct_url = json.load(f)
68
- # 读取其中的url
69
- if "url" in direct_url:
70
- url = direct_url["url"]
71
- # 处理file://开头的路径
72
- if url.startswith("file:///"):
73
- # 移除file:/// 前缀
74
- if sys.platform == "win32":
75
- # Windows 路径处理 (例如 file:///D:/path)
76
- url = url[8:] # 去除 file:///
77
- else:
78
- url = "/" + url[8:] # 保留根目录斜杠
79
- url = os.path.abspath(url)
80
- # 兼容处理旧格式 file://
81
- elif url.startswith("file://"):
82
- url = url[7:]
83
- # 对URL进行解码,处理%编码的特殊字符
84
- from urllib.parse import unquote
85
- url = unquote(url)
86
- url = os.path.abspath(url)
87
-
88
- # 确保路径格式一致
89
- if sys.platform == "win32":
90
- url = url.replace("\\", "/")
91
-
92
- return url
93
- except json.JSONDecodeError:
94
- logging.warning(f"无法解析 {direct_url_path} 的JSON内容")
95
- continue
99
+ origin_path = _decode_direct_url(str(direct_url_path))
100
+ if origin_path:
101
+ # 返回绝对路径
102
+ return origin_path
103
+ continue
96
104
  return None
97
-
105
+
98
106
  def build_dependency_tree(self, project_name: str, project_path: str, dependencies: List[str]) -> DependencyNode:
99
107
  """
100
108
  构建依赖树
@@ -171,4 +179,31 @@ class DependencyManager:
171
179
 
172
180
  def get_dependency_tree(self) -> Optional[DependencyNode]:
173
181
  """获取依赖树"""
174
- return self.root_node
182
+ return self.root_node
183
+
184
+
185
+ def find_all_mcpywrap_packages() -> List[str]:
186
+ packages = []
187
+ # 得到site-packages路径
188
+ for site_package_dir in __import__('site').getsitepackages():
189
+ site_packages = Path(site_package_dir)
190
+ for dist_info in site_packages.glob("*.dist-info"):
191
+ # 读取METADATA文件获取真实包名
192
+ metadata_path = dist_info / "METADATA"
193
+ if metadata_path.exists():
194
+ pkg_name = None
195
+ with open(metadata_path, 'r', encoding='utf-8') as f:
196
+ for line in f:
197
+ if line.startswith("Name:"):
198
+ pkg_name = line.split(":", 1)[1].strip()
199
+ break
200
+ if pkg_name is None:
201
+ continue
202
+ # 处理direct_url.json获取包路径
203
+ direct_url_path = dist_info / "direct_url.json"
204
+ if direct_url_path.exists():
205
+ origin_path = _decode_direct_url(str(direct_url_path))
206
+ if origin_path:
207
+ if check_has_mcpywrap_config(os.path.join(origin_path, CONFIG_FILE)):
208
+ packages.append(pkg_name)
209
+ return packages
@@ -121,6 +121,12 @@ def try_merge_file(source_file, target_file, source_dependency_name=None) -> Tup
121
121
  _write_lang_file(target_file, merged_lang)
122
122
  return True, f"成功合并 {base_name} 到 {os.path.basename(target_file)}"
123
123
 
124
+ if base_name.endswith('.png') or base_name.endswith('.jpg') or base_name.endswith('.jpeg') or base_name.endswith('.tga'):
125
+ # 直接覆盖,并给出警告
126
+ shutil.copy2(source_file, target_file)
127
+ click.secho(f"⚠️ 警告: {base_name} 图片文件重复,正在覆盖", fg="yellow")
128
+ return True, f"强制覆盖 {base_name} 到 {os.path.basename(target_file)}"
129
+
124
130
  # 读取源文件和目标文件的JSON内容
125
131
  source_json = _read_json_file(source_file)
126
132
  target_json = _read_json_file(target_file)