This commit is contained in:
parent
b8ddea3a9a
commit
eb9da1489d
|
|
@ -9,6 +9,11 @@ from .project_proguard import ProjectProguard
|
||||||
from .project_res_md5 import ProjectResMd5
|
from .project_res_md5 import ProjectResMd5
|
||||||
from .project_res_string import ProjectResString
|
from .project_res_string import ProjectResString
|
||||||
from .project_update import ProjectUpdate
|
from .project_update import ProjectUpdate
|
||||||
|
from .project_update_config import ProjectUpdateConfig
|
||||||
|
from .project_update_game_res import ProjectUpdateGameRes
|
||||||
|
from .project_update_icon import ProjectUpdateIcon
|
||||||
|
from .project_update_image import ProjectUpdateImage
|
||||||
|
from .project_update_keystore import ProjectUpdateKeystore
|
||||||
from .project_upload import ProjectUpload
|
from .project_upload import ProjectUpload
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -18,6 +23,11 @@ def run(context: Context):
|
||||||
ProjectInit(context),
|
ProjectInit(context),
|
||||||
ProjectCopy(context),
|
ProjectCopy(context),
|
||||||
ProjectResMd5(context),
|
ProjectResMd5(context),
|
||||||
|
ProjectUpdateKeystore(context),
|
||||||
|
ProjectUpdateConfig(context),
|
||||||
|
ProjectUpdateIcon(context),
|
||||||
|
ProjectUpdateImage(context),
|
||||||
|
ProjectUpdateGameRes(context),
|
||||||
ProjectUpdate(context),
|
ProjectUpdate(context),
|
||||||
ProjectResString(context),
|
ProjectResString(context),
|
||||||
ProjectInterface(context),
|
ProjectInterface(context),
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Context:
|
class Context:
|
||||||
|
project_original_path: str
|
||||||
|
local_repo_commit: str
|
||||||
repo_url: str = ""
|
repo_url: str = ""
|
||||||
repo_branch: str = ""
|
repo_branch: str = ""
|
||||||
repo_commit: str = ""
|
repo_commit: str = ""
|
||||||
|
|
@ -18,6 +21,7 @@ class Context:
|
||||||
project_original_path: str = "project/original"
|
project_original_path: str = "project/original"
|
||||||
|
|
||||||
temp_project_path: str = ""
|
temp_project_path: str = ""
|
||||||
|
temp_project_config_path: str = ""
|
||||||
# 本地的版本号
|
# 本地的版本号
|
||||||
local_repo_branch: str = ""
|
local_repo_branch: str = ""
|
||||||
local_repo_commit: str = ""
|
local_repo_commit: str = ""
|
||||||
|
|
@ -44,6 +48,24 @@ class Context:
|
||||||
|
|
||||||
string: dict = None
|
string: dict = None
|
||||||
|
|
||||||
|
update_code: bool = True
|
||||||
|
update_config: bool = True
|
||||||
|
update_keystore: bool = True
|
||||||
|
update_res_img: bool = True
|
||||||
|
update_res_icon: bool = True
|
||||||
|
update_res_unity: bool = True
|
||||||
|
config_path: str = ""
|
||||||
|
keystore_path: str = ""
|
||||||
|
res_img_path: str = ""
|
||||||
|
res_icon_path: str = ""
|
||||||
|
res_unity_path: str = ""
|
||||||
|
|
||||||
|
config_config_md5: str = ""
|
||||||
|
config_keystore_md5: str = ""
|
||||||
|
config_res_img_md5: str = ""
|
||||||
|
config_res_icon_md5: str = ""
|
||||||
|
config_res_unity_md5: str = ""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, json_str: str):
|
def from_json(cls, json_str: str):
|
||||||
data = json.loads(json_str)
|
data = json.loads(json_str)
|
||||||
|
|
@ -58,3 +80,37 @@ class Context:
|
||||||
if self.app_name:
|
if self.app_name:
|
||||||
return self.app_name
|
return self.app_name
|
||||||
return self.get_config("app_name")
|
return self.get_config("app_name")
|
||||||
|
|
||||||
|
def get_cache_config(self) -> dict[str, str]:
|
||||||
|
try:
|
||||||
|
path = os.path.join(self.temp_project_config_path, "config.json")
|
||||||
|
args = json.load(open(path, "r", encoding="utf-8"))
|
||||||
|
return args
|
||||||
|
except:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def get_cache_config_from_key(self, key: str, default: str = "") -> str:
|
||||||
|
try:
|
||||||
|
return self.get_cache_config().get(key, default)
|
||||||
|
except:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def save_cache_config(self, key: str, value: str):
|
||||||
|
path = os.path.join(self.temp_project_config_path, "config.json")
|
||||||
|
config = self.get_cache_config()
|
||||||
|
config[key] = value
|
||||||
|
json.dump(config, open(path, "w", encoding="utf-8"), indent=4)
|
||||||
|
|
||||||
|
def get_map(self) -> dict[str, str]:
|
||||||
|
try:
|
||||||
|
path = os.path.join(self.temp_project_config_path, "map.json")
|
||||||
|
return json.load(open(path, "r", encoding="utf-8"))
|
||||||
|
except:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def save_map(self, map: dict[str, str]):
|
||||||
|
path = os.path.join(self.temp_project_config_path, "map.json")
|
||||||
|
json.dump(map, open(path, "w", encoding="utf-8"), indent=4)
|
||||||
|
|
||||||
|
def get_map_from_key(self, file_name) -> str:
|
||||||
|
return self.get_map().get(file_name, file_name)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class ProjectBuild(Task):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.init()
|
self.init()
|
||||||
# self.save_project()
|
# self.save_project()
|
||||||
self.build_apk()
|
# self.build_apk()
|
||||||
self.build_aab()
|
self.build_aab()
|
||||||
self.copy_to_out()
|
self.copy_to_out()
|
||||||
pass
|
pass
|
||||||
|
|
@ -54,12 +54,12 @@ class ProjectBuild(Task):
|
||||||
|
|
||||||
def copy_to_out(self):
|
def copy_to_out(self):
|
||||||
app_logger().debug(f"copy_to_out start.")
|
app_logger().debug(f"copy_to_out start.")
|
||||||
target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}apk{os.sep}lawnWithQuickstepPlay{os.sep}debug"
|
# target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}apk{os.sep}lawnWithQuickstepPlay{os.sep}debug"
|
||||||
FileUtils.copy(find_path(target, "apk"), self.context.out_debug_apk)
|
# FileUtils.copy(find_path(target, "apk"), self.context.out_debug_apk)
|
||||||
app_logger().debug(f"copy_to_out debug apk = {self.context.out_debug_apk}")
|
# app_logger().debug(f"copy_to_out debug apk = {self.context.out_debug_apk}")
|
||||||
target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}apk{os.sep}lawnWithQuickstepPlay{os.sep}release"
|
# target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}apk{os.sep}lawnWithQuickstepPlay{os.sep}release"
|
||||||
FileUtils.copy(find_path(target, "apk"), self.context.out_release_apk)
|
# FileUtils.copy(find_path(target, "apk"), self.context.out_release_apk)
|
||||||
app_logger().debug(f"copy_to_out release apk = {self.context.out_release_apk}")
|
# app_logger().debug(f"copy_to_out release apk = {self.context.out_release_apk}")
|
||||||
target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}bundle{os.sep}lawnWithQuickstepPlayRelease"
|
target = f"{self.context.temp_project_path}{os.sep}build{os.sep}outputs{os.sep}bundle{os.sep}lawnWithQuickstepPlayRelease"
|
||||||
FileUtils.copy(find_path(target, "aab"), self.context.out_release_aab)
|
FileUtils.copy(find_path(target, "aab"), self.context.out_release_aab)
|
||||||
app_logger().debug(f"copy_to_out end.")
|
app_logger().debug(f"copy_to_out end.")
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,63 @@
|
||||||
|
import os.path
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from git import Repo
|
||||||
|
|
||||||
from scripts.task import Task
|
from scripts.task import Task
|
||||||
from utils import FileUtils
|
from utils import FileUtils
|
||||||
from utils import TimeUtils
|
|
||||||
from utils.logger_utils import app_logger
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
|
def clean_workspace(repo):
|
||||||
|
# 1. 放弃工作区所有已跟踪文件的修改(包括恢复被删除的已跟踪文件)
|
||||||
|
# - checkout 命令会将工作区文件重置为当前 HEAD 版本的状态
|
||||||
|
repo.git.checkout('.')
|
||||||
|
|
||||||
|
# 2. 删除工作区所有未跟踪的文件和目录(包括空目录)
|
||||||
|
# - -f:强制删除(避免询问)
|
||||||
|
# - -d:同时删除未跟踪的目录
|
||||||
|
repo.git.clean('-fd')
|
||||||
|
|
||||||
|
print("工作空间已清空,恢复到当前分支最新提交状态")
|
||||||
|
|
||||||
|
|
||||||
class ProjectCopy(Task):
|
class ProjectCopy(Task):
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
self.init()
|
if os.path.exists(self.context.temp_project_path):
|
||||||
|
temp_path = os.path.join(self.context.temp_project_path, "build", "outputs")
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
shutil.rmtree(temp_path)
|
||||||
|
|
||||||
|
temp_path = os.path.join(self.context.temp_project_path, "build", "generated")
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
shutil.rmtree(temp_path)
|
||||||
|
|
||||||
|
app_logger().debug("project '{}' to '{}'".format(self.context.temp_project_path, "项目已经存在了"))
|
||||||
|
repo = Repo(self.context.temp_project_path)
|
||||||
|
temp_repo_commit = repo.head.commit.hexsha[:10]
|
||||||
|
app_logger().debug(
|
||||||
|
f"project '{self.context.temp_project_path}' , local '{temp_repo_commit}' remote '{self.context.local_repo_commit}")
|
||||||
|
if temp_repo_commit != self.context.local_repo_commit:
|
||||||
|
app_logger().debug("本地代码有变动,需要更新临时的目录")
|
||||||
|
# 本地分支和远程的分支不一样
|
||||||
|
clean_workspace(repo)
|
||||||
|
origin = repo.remote("origin")
|
||||||
|
origin.fetch()
|
||||||
|
repo.git.checkout(self.context.local_repo_commit)
|
||||||
|
self.context.update_code = True
|
||||||
|
|
||||||
|
self.context.update_config = True
|
||||||
|
self.context.update_keystore = True
|
||||||
|
self.context.update_res_img = True
|
||||||
|
self.context.update_res_icon = True
|
||||||
|
self.context.update_res_unity = True
|
||||||
|
else:
|
||||||
|
self.context.update_code = False
|
||||||
|
app_logger().debug("本地的代码没有变动,无需更新")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.context.update_code = True
|
||||||
result = FileUtils.copy(self.context.project_original_path, self.context.temp_project_path)
|
result = FileUtils.copy(self.context.project_original_path, self.context.temp_project_path)
|
||||||
app_logger().debug("Copied project '{}' to '{}'".format(self.context.project_original_path, result))
|
app_logger().debug("Copied project '{}' to '{}'".format(self.context.project_original_path, result))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def init(self):
|
|
||||||
self.context.temp_project_path = self.context.project_original_path.replace(
|
|
||||||
"original", self.context.package_name.replace(".", "_") + TimeUtils.get_formatted_time(format_str="%H%M%S")
|
|
||||||
)
|
|
||||||
# self.context.temp_project_path = self.context.project_original_path
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ from utils import FileUtils
|
||||||
|
|
||||||
class ProjectEnd(Task):
|
class ProjectEnd(Task):
|
||||||
def execute(self):
|
def execute(self):
|
||||||
FileUtils.delete(self.context.temp_project_path, True)
|
# FileUtils.delete(self.context.temp_project_path, True)
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from git import Repo, RemoteProgress
|
from git import Repo, RemoteProgress
|
||||||
|
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
from .context import Context
|
||||||
from .task import Task
|
from .task import Task
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,7 +15,72 @@ def progress(op_code, cur_count, max_count=None, message=''):
|
||||||
print(f"操作: {op_code}, 进度: {cur_count}/{max_count}, 消息: {message}")
|
print(f"操作: {op_code}, 进度: {cur_count}/{max_count}, 消息: {message}")
|
||||||
|
|
||||||
|
|
||||||
|
def check_config_exists(*file_paths):
|
||||||
|
# game_config/com.diy.emoticon.free.zcaqf.game.launcher/com.diy.emoticon.free.zcaqf.game.launcher_android.zip
|
||||||
|
# game_config/com.emoticon.diy.znfav.launcher.free/com.emoticon.diy.znfav.launcher.free_android.zip
|
||||||
|
# 遍历检查每个文件
|
||||||
|
if file_paths is None:
|
||||||
|
file_paths = []
|
||||||
|
for file_path in file_paths:
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
# 抛出异常并说明缺失的文件路径
|
||||||
|
raise FileNotFoundError(f"配置文件不存在: {file_path}")
|
||||||
|
|
||||||
|
|
||||||
class ProjectInit(Task):
|
class ProjectInit(Task):
|
||||||
|
|
||||||
|
def __init__(self, context: Context):
|
||||||
|
super().__init__(context)
|
||||||
|
self.context.temp_project_path = self.context.project_original_path.replace("original",
|
||||||
|
"V1_" + self.context.package_name.replace(
|
||||||
|
".", "_"))
|
||||||
|
self.context.temp_project_config_path = self.context.temp_project_path + "_config"
|
||||||
|
if not os.path.exists(self.context.temp_project_config_path):
|
||||||
|
os.makedirs(self.context.temp_project_config_path)
|
||||||
|
|
||||||
|
config_md5 = context.get_cache_config_from_key("config", "")
|
||||||
|
keystore_md5 = context.get_cache_config_from_key("keystore", "")
|
||||||
|
res_img_md5 = context.get_cache_config_from_key("res_img", "")
|
||||||
|
res_icon_md5 = context.get_cache_config_from_key("res_icon", "")
|
||||||
|
res_unity_md5 = context.get_cache_config_from_key("res_unity", "")
|
||||||
|
|
||||||
|
app_logger().debug(f"temp project res md5 , "
|
||||||
|
f"config : {config_md5} , "
|
||||||
|
f"keystore : {keystore_md5} , "
|
||||||
|
f"res_img : {res_img_md5} , "
|
||||||
|
f"res_icon : {res_icon_md5} , "
|
||||||
|
f"res_unity : {res_unity_md5}")
|
||||||
|
|
||||||
|
config_root_path = os.path.join("game_config", self.context.package_name)
|
||||||
|
|
||||||
|
self.context.config_path = os.path.join(config_root_path, f"{self.context.package_name}_android.zip")
|
||||||
|
self.context.keystore_path = ""
|
||||||
|
for i in os.listdir(config_root_path):
|
||||||
|
if i.endswith(".keystore"):
|
||||||
|
self.context.keystore_path = os.path.join(config_root_path, i)
|
||||||
|
|
||||||
|
self.context.res_img_path = os.path.join(config_root_path, "drawable-xxhdpi.zip")
|
||||||
|
self.context.res_icon_path = os.path.join(config_root_path, "icon.zip")
|
||||||
|
self.context.res_unity_path = os.path.join(config_root_path, "unityLibrary.zip")
|
||||||
|
|
||||||
|
check_config_exists(self.context.config_path,
|
||||||
|
self.context.keystore_path,
|
||||||
|
self.context.res_img_path,
|
||||||
|
self.context.res_icon_path,
|
||||||
|
self.context.res_unity_path)
|
||||||
|
|
||||||
|
self.context.config_config_md5 = FileUtils.get_md5(self.context.config_path)
|
||||||
|
self.context.config_keystore_md5 = FileUtils.get_md5(self.context.keystore_path)
|
||||||
|
self.context.config_res_img_md5 = FileUtils.get_md5(self.context.res_img_path)
|
||||||
|
self.context.config_res_icon_md5 = FileUtils.get_md5(self.context.res_icon_path)
|
||||||
|
self.context.config_res_unity_md5 = FileUtils.get_md5(self.context.res_unity_path)
|
||||||
|
|
||||||
|
self.context.update_config = config_md5 != self.context.config_config_md5
|
||||||
|
self.context.update_keystore = keystore_md5 != self.context.config_keystore_md5
|
||||||
|
self.context.update_res_img = res_img_md5 != self.context.config_res_img_md5
|
||||||
|
self.context.update_res_icon = res_icon_md5 != self.context.config_res_icon_md5
|
||||||
|
self.context.update_res_unity = res_unity_md5 != self.context.config_res_unity_md5
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
try:
|
try:
|
||||||
repo = Repo(self.context.project_original_path)
|
repo = Repo(self.context.project_original_path)
|
||||||
|
|
@ -45,12 +115,13 @@ class ProjectInit(Task):
|
||||||
local_branch.set_tracking_branch(repo.remotes[remote_name].refs[branch_name]) # 设置跟踪
|
local_branch.set_tracking_branch(repo.remotes[remote_name].refs[branch_name]) # 设置跟踪
|
||||||
local_branch.checkout() # 切换到该分支
|
local_branch.checkout() # 切换到该分支
|
||||||
|
|
||||||
|
# 拉取最新代码
|
||||||
|
repo.remotes.origin.pull()
|
||||||
|
|
||||||
self.context.local_repo_branch = repo.active_branch.name
|
self.context.local_repo_branch = repo.active_branch.name
|
||||||
self.context.local_repo_commit = repo.head.commit.hexsha[:10]
|
self.context.local_repo_commit = repo.head.commit.hexsha[:10]
|
||||||
|
|
||||||
# 拉取最新代码
|
print("当前分支:" + repo.active_branch.name, self.context.local_repo_commit)
|
||||||
repo.remotes.origin.pull()
|
|
||||||
print("当前分支:" + repo.active_branch.name)
|
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise Exception(f"No commit to {self.context.repo_commit}")
|
raise Exception(f"No commit to {self.context.repo_commit}")
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import os
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from utils import FileUtils
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
def string_to_md5(text):
|
def string_to_md5(text):
|
||||||
|
|
@ -65,6 +66,9 @@ class ProjectInterface(Task):
|
||||||
f_out.write(line)
|
f_out.write(line)
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
if not self.context.update_code:
|
||||||
|
app_logger().info("代码没有更新,不需要处理 unity 接口")
|
||||||
|
return
|
||||||
|
|
||||||
self.unity_proxy_api_file = os.path.join(self.context.temp_project_path,
|
self.unity_proxy_api_file = os.path.join(self.context.temp_project_path,
|
||||||
"launcher-game/src/com/game/hachisdk/unity/UnityProxyApi.java".replace(
|
"launcher-game/src/com/game/hachisdk/unity/UnityProxyApi.java".replace(
|
||||||
|
|
|
||||||
|
|
@ -276,6 +276,10 @@ class ProjectProguard(Task):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
if not self.context.update_code:
|
||||||
|
app_logger().info("No update project proguard found")
|
||||||
|
self.context.proguard_dict = self.context.get_map()
|
||||||
|
return
|
||||||
self.root = self.context.temp_project_path
|
self.root = self.context.temp_project_path
|
||||||
self.module_path = os.path.join(self.root, "launcher-game")
|
self.module_path = os.path.join(self.root, "launcher-game")
|
||||||
self.code_path = os.path.join(self.module_path, "src")
|
self.code_path = os.path.join(self.module_path, "src")
|
||||||
|
|
@ -324,6 +328,7 @@ class ProjectProguard(Task):
|
||||||
|
|
||||||
encrypt_xml_resources(self.string_path, False, string_to_md5(self.context.package_name).upper())
|
encrypt_xml_resources(self.string_path, False, string_to_md5(self.context.package_name).upper())
|
||||||
|
|
||||||
|
self.context.save_map(self.context.proguard_dict)
|
||||||
app_logger().info(json.dumps(self.context.proguard_dict, indent=4))
|
app_logger().info(json.dumps(self.context.proguard_dict, indent=4))
|
||||||
|
|
||||||
# if __name__ == '__main__':
|
# if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import hashlib
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
def get_md5(file_path):
|
def get_md5(file_path):
|
||||||
"""计算文件的MD5值"""
|
"""计算文件的MD5值"""
|
||||||
|
|
@ -64,6 +66,9 @@ def modify_one_pixel(input_path, output_path):
|
||||||
class ProjectResMd5(Task):
|
class ProjectResMd5(Task):
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
if not self.context.update_code:
|
||||||
|
app_logger().info("不需要更新res md5")
|
||||||
|
return
|
||||||
path = f"launcher-game/res/"
|
path = f"launcher-game/res/"
|
||||||
root_path = os.path.join(self.context.temp_project_path, path)
|
root_path = os.path.join(self.context.temp_project_path, path)
|
||||||
for i in os.listdir(root_path):
|
for i in os.listdir(root_path):
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ class ProjectResString(Task):
|
||||||
app_logger().debug("路径不存,不操作了,后续可以给他创建出来:" + string_path + "\t" + json.dumps(res, indent=4))
|
app_logger().debug("路径不存,不操作了,后续可以给他创建出来:" + string_path + "\t" + json.dumps(res, indent=4))
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
|
if not self.context.update_code:
|
||||||
|
app_logger().info("代码没有更新,不需要处理资源")
|
||||||
|
return
|
||||||
for key in self.context.string.keys():
|
for key in self.context.string.keys():
|
||||||
launcher = key.replace('base', '')
|
launcher = key.replace('base', '')
|
||||||
if launcher:
|
if launcher:
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,11 @@
|
||||||
import os.path
|
import os.path
|
||||||
from pathlib import Path
|
|
||||||
import re
|
import re
|
||||||
from xml.dom import minidom
|
|
||||||
|
|
||||||
import javaproperties
|
|
||||||
import requests
|
|
||||||
from lxml import etree
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
from scripts.context import Context
|
from scripts.context import Context
|
||||||
from scripts.task import Task
|
from scripts.task import Task
|
||||||
from utils import FileUtils
|
|
||||||
from utils.logger_utils import app_logger
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
def process_manifest(input_path, output_path):
|
|
||||||
# 解析XML文件
|
|
||||||
tree = ET.parse(input_path)
|
|
||||||
root = tree.getroot()
|
|
||||||
|
|
||||||
# 定义命名空间映射
|
|
||||||
android_namespace = 'http://schemas.android.com/apk/res/android'
|
|
||||||
ET.register_namespace('android', android_namespace)
|
|
||||||
namespaces = {'android': android_namespace}
|
|
||||||
|
|
||||||
# 处理application标签,移除所有属性
|
|
||||||
application = root.find('application')
|
|
||||||
if application is not None:
|
|
||||||
# 保存所有子节点
|
|
||||||
children = list(application)
|
|
||||||
# 清除application标签
|
|
||||||
root.remove(application)
|
|
||||||
# 创建新的application标签(无属性)
|
|
||||||
new_application = ET.Element('application')
|
|
||||||
# 添加回所有子节点
|
|
||||||
for child in children:
|
|
||||||
new_application.append(child)
|
|
||||||
# 将新的application标签添加回root
|
|
||||||
root.append(new_application)
|
|
||||||
|
|
||||||
# 查找并删除指定的activity节点
|
|
||||||
activity_to_remove = new_application.find(
|
|
||||||
".//activity[@android:name='com.unity3d.player.UnityPlayerActivity']",
|
|
||||||
namespaces=namespaces
|
|
||||||
)
|
|
||||||
if activity_to_remove is not None:
|
|
||||||
new_application.remove(activity_to_remove)
|
|
||||||
|
|
||||||
# 保存处理后的XML,保留android命名空间前缀
|
|
||||||
rough_string = ET.tostring(root, 'utf-8')
|
|
||||||
reparsed = minidom.parseString(rough_string)
|
|
||||||
pretty_xml = reparsed.toprettyxml(indent=" ", encoding='utf-8')
|
|
||||||
|
|
||||||
# 去除空行
|
|
||||||
lines = pretty_xml.decode('utf-8').split('\n')
|
|
||||||
non_empty_lines = [line for line in lines if line.strip() != '']
|
|
||||||
pretty_xml = '\n'.join(non_empty_lines).encode('utf-8')
|
|
||||||
|
|
||||||
with open(output_path, 'wb') as f:
|
|
||||||
f.write(pretty_xml)
|
|
||||||
|
|
||||||
print(f"处理完成,结果已保存到 {output_path}")
|
|
||||||
|
|
||||||
|
|
||||||
def uncomment_line(content, line_pattern):
|
|
||||||
"""
|
|
||||||
取消指定行的注释
|
|
||||||
|
|
||||||
:param content: 文件内容
|
|
||||||
:param line_pattern: 要取消注释的行内容(不含前导//和空格)
|
|
||||||
:return: 更新后的内容
|
|
||||||
"""
|
|
||||||
# 匹配以//开头,后跟任意空格,然后是目标行内容
|
|
||||||
pattern = rf'^(\s*)//\s*({re.escape(line_pattern)}\s*)$'
|
|
||||||
|
|
||||||
# 替换为去注释版本(保留原有缩进)
|
|
||||||
replacement = rf'\1\2'
|
|
||||||
|
|
||||||
updated_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
|
|
||||||
return updated_content
|
|
||||||
|
|
||||||
|
|
||||||
def update_gradle_variable(content, variable_name, new_value):
|
def update_gradle_variable(content, variable_name, new_value):
|
||||||
"""
|
"""
|
||||||
更新 Gradle 文件中的 final def 变量值
|
更新 Gradle 文件中的 final def 变量值
|
||||||
|
|
@ -166,12 +91,8 @@ def update_gradle_property(content, key, new_value):
|
||||||
return updated_content
|
return updated_content
|
||||||
|
|
||||||
|
|
||||||
LAUNCHER_CODE_PATH = f"LauncherCode/src/com/launchercode".replace("/", os.sep)
|
|
||||||
GAME_ACTIVITY_PATH = f"launcher-game/src/com/game/launcher/activity/GLGameActivity.kt".replace("/", os.sep)
|
|
||||||
GAME_ACTIVITY_PATH_2 = f"LauncherCode/src/com/launchercode/activity/GameActivity.kt".replace("/", os.sep)
|
GAME_ACTIVITY_PATH_2 = f"LauncherCode/src/com/launchercode/activity/GameActivity.kt".replace("/", os.sep)
|
||||||
ANDROID_MANIFEST_PATH = f"launcher-game/AndroidManifest.xml".replace("/", os.sep)
|
|
||||||
STRING_PATH = f"launcher-game/res/values/strings.xml".replace("/", os.sep)
|
STRING_PATH = f"launcher-game/res/values/strings.xml".replace("/", os.sep)
|
||||||
LAUNCER_STRING_PATH = f"LauncherCode/src/com/launchercode/LauncherStringsValue.kt".replace("/", os.sep)
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdate(Task):
|
class ProjectUpdate(Task):
|
||||||
|
|
@ -181,6 +102,9 @@ class ProjectUpdate(Task):
|
||||||
self.build_gradle_path = None
|
self.build_gradle_path = None
|
||||||
|
|
||||||
def update_package_name(self):
|
def update_package_name(self):
|
||||||
|
if not self.context.update_config:
|
||||||
|
app_logger().info("配置文件没有变动,package不需要更新")
|
||||||
|
return
|
||||||
"""
|
"""
|
||||||
更新包名
|
更新包名
|
||||||
:return:
|
:return:
|
||||||
|
|
@ -203,209 +127,6 @@ class ProjectUpdate(Task):
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update_keystore(self):
|
|
||||||
root_dir = os.path.join("game_config", self.context.package_name)
|
|
||||||
for file in os.listdir(root_dir):
|
|
||||||
if file.endswith(".keystore"):
|
|
||||||
name = file.replace(".keystore", "")
|
|
||||||
FileUtils.copy(os.path.join(root_dir, file), os.path.join(self.context.temp_project_path, file))
|
|
||||||
|
|
||||||
open(os.path.join(self.context.temp_project_path, "keystore.properties"), "w", encoding="utf-8").write(
|
|
||||||
f"""
|
|
||||||
keyAlias={name}
|
|
||||||
keyPassword=123456
|
|
||||||
storeFile=./{name}.keystore
|
|
||||||
storePassword=123456
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise Exception("keystore not found")
|
|
||||||
|
|
||||||
def update_config(self):
|
|
||||||
"""
|
|
||||||
更新配置文件
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
root_dir = os.path.join("game_config", self.context.package_name)
|
|
||||||
config_path = list(
|
|
||||||
filter(lambda f: f.endswith(".zip") and f.startswith(self.context.package_name), os.listdir(root_dir)))
|
|
||||||
if len(config_path) <= 0:
|
|
||||||
raise Exception("config not found")
|
|
||||||
|
|
||||||
target_path = os.path.join(root_dir, config_path[0])
|
|
||||||
|
|
||||||
dst = os.path.join(self.context.temp_project_path, config_path[0].replace(".zip", ""))
|
|
||||||
|
|
||||||
result = FileUtils.decompress(target_path, dst)
|
|
||||||
app_logger().debug(f"{target_path} -> {dst} , 解压结果: {result}")
|
|
||||||
|
|
||||||
mainly_path = os.path.join(dst, "mainly")
|
|
||||||
if not os.path.exists(mainly_path):
|
|
||||||
mainly_path = os.path.join(dst, "appConfig")
|
|
||||||
|
|
||||||
google_services_json_path = os.path.join(dst, "google-services.json")
|
|
||||||
if not os.path.exists(google_services_json_path):
|
|
||||||
google_services_json_path = os.path.join(dst, "appConfig", "google-services.json")
|
|
||||||
|
|
||||||
FileUtils.copy(google_services_json_path,
|
|
||||||
os.path.join(self.context.temp_project_path, "google-services.json"),
|
|
||||||
True)
|
|
||||||
|
|
||||||
dst_path = os.path.join(self.context.temp_project_path, f"launcher-game{os.sep}assets")
|
|
||||||
|
|
||||||
for file in list(filter(lambda f: not (f == "google_fonts.json" or f == "pag_gl_slide.pag"),
|
|
||||||
os.listdir(dst_path))):
|
|
||||||
FileUtils.delete(os.path.join(dst_path, file), True)
|
|
||||||
pass
|
|
||||||
|
|
||||||
for file in list(filter(lambda f: f.find(".") <= 0, os.listdir(mainly_path))):
|
|
||||||
FileUtils.copy(os.path.join(mainly_path, file), os.path.join(dst_path, file))
|
|
||||||
|
|
||||||
with open(os.path.join(dst, "tkg_config_mainly.properties"), 'rb') as f:
|
|
||||||
self.context.config = javaproperties.load(f)
|
|
||||||
|
|
||||||
# 不打admob
|
|
||||||
if self.context.admob_app_id is None or self.context.admob_app_id == "":
|
|
||||||
self.context.admob_app_id = self.context.get_config("admob_id")
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_icon(self):
|
|
||||||
"""
|
|
||||||
更新游戏Icon
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
target_icon_path = os.path.join("game_config", self.context.package_name, "icon.zip")
|
|
||||||
tag = "res_icon_resources"
|
|
||||||
dst = os.path.join(self.context.temp_project_path, tag)
|
|
||||||
FileUtils.decompress(target_icon_path, dst)
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk(dst):
|
|
||||||
for file in files:
|
|
||||||
temp_tart_path = os.path.join(root, file)
|
|
||||||
if temp_tart_path.find("__MACOSX") > 0:
|
|
||||||
continue
|
|
||||||
temp_dst = temp_tart_path.replace(tag, "launcher-game" + os.sep + "res")
|
|
||||||
app_logger().debug(f"copy icon = {temp_tart_path} -> {temp_dst}")
|
|
||||||
FileUtils.copy(temp_tart_path, temp_dst, True)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_game_result(self):
|
|
||||||
"""
|
|
||||||
更新游戏资源
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
root_dir = os.path.join("game_config", self.context.package_name)
|
|
||||||
|
|
||||||
if self.context.game_type == "unity_native":
|
|
||||||
res_path = os.path.join(root_dir, "unityLibrary.zip")
|
|
||||||
if not os.path.exists(res_path):
|
|
||||||
raise Exception("unity library not found")
|
|
||||||
dst = os.path.join(self.context.temp_project_path, "unityLibrary")
|
|
||||||
temp_dst = dst + "_res"
|
|
||||||
if os.path.exists(dst):
|
|
||||||
FileUtils.delete(dst, True)
|
|
||||||
|
|
||||||
if os.path.exists(temp_dst):
|
|
||||||
FileUtils.delete(temp_dst, True)
|
|
||||||
|
|
||||||
FileUtils.decompress(res_path, temp_dst)
|
|
||||||
|
|
||||||
build_path = os.path.join(temp_dst, "build")
|
|
||||||
if os.path.exists(build_path):
|
|
||||||
FileUtils.delete(build_path, True)
|
|
||||||
|
|
||||||
if os.listdir(temp_dst).index("unityLibrary") >= 0:
|
|
||||||
FileUtils.copy(os.path.join(temp_dst, "unityLibrary"), dst)
|
|
||||||
else:
|
|
||||||
FileUtils.copy(temp_dst, dst)
|
|
||||||
|
|
||||||
android_manifest_xml_path = os.path.join(dst, "src", "main", "AndroidManifest.xml")
|
|
||||||
process_manifest(android_manifest_xml_path, android_manifest_xml_path)
|
|
||||||
|
|
||||||
text = open(os.path.join(dst, "build.gradle"), "r", encoding="utf-8").read()
|
|
||||||
text = text.replace("implementation", "api")
|
|
||||||
if not 'namespace "com.unity3d.player"' in text:
|
|
||||||
text = text.replace("compileSdkVersion", """
|
|
||||||
namespace "com.unity3d.player"
|
|
||||||
compileSdkVersion""")
|
|
||||||
|
|
||||||
text = text.replace("unityStreamingAssets.tokenize(', ')",
|
|
||||||
'[".unity3d", ".bundle", ".version", ".bytes", ".hash"]')
|
|
||||||
text = text.replace("apply plugin: 'com.android.library'", """
|
|
||||||
plugins {
|
|
||||||
id 'com.android.library'
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
open(os.path.join(dst, "build.gradle"), "w", encoding="utf-8").write(text)
|
|
||||||
|
|
||||||
lines = open(os.path.join(dst, "build.gradle"), "r", encoding="utf-8").readlines()
|
|
||||||
new_lines = []
|
|
||||||
for line in lines:
|
|
||||||
if line.find("com.game:hachisdk") > 0:
|
|
||||||
continue
|
|
||||||
new_lines.append(line)
|
|
||||||
open(os.path.join(dst, "build.gradle"), "w", encoding="utf-8").writelines(new_lines)
|
|
||||||
|
|
||||||
# 引用Unity项目
|
|
||||||
text = open(os.path.join(self.context.temp_project_path, "ad.gradle"), "r", encoding="utf-8").read()
|
|
||||||
text = uncomment_line(text, "implementation projects.unityLibrary")
|
|
||||||
open(os.path.join(self.context.temp_project_path, "ad.gradle"), "w", encoding="utf-8").write(text)
|
|
||||||
|
|
||||||
text = open(os.path.join(self.context.temp_project_path, "settings.gradle"), "r", encoding="utf-8").read()
|
|
||||||
text = uncomment_line(text, "include ':unityLibrary'")
|
|
||||||
open(os.path.join(self.context.temp_project_path, "settings.gradle"), "w", encoding="utf-8").write(text)
|
|
||||||
|
|
||||||
# launcher 引用 unityActivity
|
|
||||||
text = open(os.path.join(self.context.temp_project_path, GAME_ACTIVITY_PATH), "r", encoding="utf-8").read()
|
|
||||||
text = text.replace("GLGameWebActivity", "com.unity3d.player.UnityPlayerActivity")
|
|
||||||
open(os.path.join(self.context.temp_project_path, GAME_ACTIVITY_PATH), "w", encoding="utf-8").write(text)
|
|
||||||
|
|
||||||
text = open(os.path.join(self.context.temp_project_path, ANDROID_MANIFEST_PATH), "r",
|
|
||||||
encoding="utf-8").read()
|
|
||||||
text = text.replace("@style/LauncherGameIntroTheme", "@style/UnityThemeSelector")
|
|
||||||
open(os.path.join(self.context.temp_project_path, ANDROID_MANIFEST_PATH), "w", encoding="utf-8").write(text)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise Exception(f"不支持的游戏类型 : {self.context.game_type}")
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_image(self):
|
|
||||||
"""
|
|
||||||
更新游戏的资源
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
root_dir = os.path.join("game_config", self.context.package_name)
|
|
||||||
drawable_path = os.path.join(root_dir, "drawable-xxhdpi.zip")
|
|
||||||
if not os.path.exists(drawable_path):
|
|
||||||
raise Exception("drawable not found")
|
|
||||||
dst = os.path.join(self.context.temp_project_path, "drawable_res")
|
|
||||||
FileUtils.decompress(drawable_path, dst)
|
|
||||||
|
|
||||||
if os.path.join(dst, "drawable-xxhdpi"):
|
|
||||||
dst = os.path.join(dst, "drawable-xxhdpi")
|
|
||||||
|
|
||||||
target_root_path = os.path.join(self.context.temp_project_path,
|
|
||||||
f"launcher-game{os.sep}res{os.sep}drawable-xxhdpi")
|
|
||||||
|
|
||||||
image_list = list(map(lambda f: Path(f).stem, os.listdir(target_root_path)))
|
|
||||||
|
|
||||||
for file in os.listdir(dst):
|
|
||||||
if file == ".DS_Store":
|
|
||||||
continue
|
|
||||||
temp_tar = os.path.join(dst, file)
|
|
||||||
temp_dst = os.path.join(target_root_path, file)
|
|
||||||
file_name = Path(file).stem
|
|
||||||
if file_name in image_list:
|
|
||||||
FileUtils.delete(os.path.join(target_root_path, file_name + ".png"))
|
|
||||||
FileUtils.delete(os.path.join(target_root_path, file_name + ".jpg"))
|
|
||||||
pass
|
|
||||||
FileUtils.copy(temp_tar, temp_dst, True)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_gradle_config(self):
|
def update_gradle_config(self):
|
||||||
"""
|
"""
|
||||||
更新gradle里面的版本号
|
更新gradle里面的版本号
|
||||||
|
|
@ -427,6 +148,9 @@ plugins {
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update_string(self):
|
def update_string(self):
|
||||||
|
if not self.context.update_config:
|
||||||
|
app_logger().info("配置文件没有变动,string不需要更新")
|
||||||
|
return
|
||||||
privacy = self.context.get_config("TkA_Url_Privacy")
|
privacy = self.context.get_config("TkA_Url_Privacy")
|
||||||
if not privacy or privacy == "":
|
if not privacy or privacy == "":
|
||||||
raise Exception("配置文件中没有配置 TkA_Url_Privacy")
|
raise Exception("配置文件中没有配置 TkA_Url_Privacy")
|
||||||
|
|
@ -440,29 +164,11 @@ plugins {
|
||||||
text = text.replace("https://doanvanquy.com/TermsOfUse.html",
|
text = text.replace("https://doanvanquy.com/TermsOfUse.html",
|
||||||
privacy.replace("privacy.html", "TermsOfUse.html"))
|
privacy.replace("privacy.html", "TermsOfUse.html"))
|
||||||
open(os.path.join(self.context.temp_project_path, STRING_PATH), "w", encoding="utf-8").write(text)
|
open(os.path.join(self.context.temp_project_path, STRING_PATH), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
# text = open(os.path.join(self.context.temp_project_path, LAUNCER_STRING_PATH), "r", encoding="utf-8").read()
|
|
||||||
# text = text.replace("https://harmonitun.com/privacy.html", privacy)
|
|
||||||
# text = text.replace("https://harmonitun.com/TermsOfUse.html",
|
|
||||||
# privacy.replace("privacy.html", "TermsOfUse.html"))
|
|
||||||
# text = text.replace("harmounitun@outlook.com", tkg_custom)
|
|
||||||
# open(os.path.join(self.context.temp_project_path, LAUNCER_STRING_PATH), "w", encoding="utf-8").write(text)
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
global GAME_ACTIVITY_PATH
|
|
||||||
path = os.path.join(self.context.temp_project_path, GAME_ACTIVITY_PATH)
|
|
||||||
if not os.path.exists(path):
|
|
||||||
GAME_ACTIVITY_PATH = GAME_ACTIVITY_PATH_2
|
|
||||||
# GAME_ACTIVITY_PATH = GAME_ACTIVITY_PATH_2
|
|
||||||
pass
|
|
||||||
self.build_gradle_path = os.path.join(self.context.temp_project_path, "build.gradle")
|
self.build_gradle_path = os.path.join(self.context.temp_project_path, "build.gradle")
|
||||||
self.update_package_name()
|
self.update_package_name()
|
||||||
self.update_keystore()
|
|
||||||
self.update_config()
|
|
||||||
self.update_icon()
|
|
||||||
self.update_image()
|
|
||||||
self.update_game_result()
|
|
||||||
self.update_gradle_config()
|
self.update_gradle_config()
|
||||||
self.update_string()
|
self.update_string()
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import javaproperties
|
||||||
|
|
||||||
|
from scripts.task import Task
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateConfig(Task):
|
||||||
|
|
||||||
|
def update_config(self):
|
||||||
|
if not self.context.update_config:
|
||||||
|
app_logger().info("配置文件没有更新")
|
||||||
|
return
|
||||||
|
|
||||||
|
"""
|
||||||
|
更新配置文件
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
target_path = self.context.config_path
|
||||||
|
|
||||||
|
dst = os.path.join(self.context.temp_project_path, os.path.basename(target_path).replace(".zip", ""))
|
||||||
|
|
||||||
|
if not self.context.update_config:
|
||||||
|
app_logger().info("No update config found")
|
||||||
|
|
||||||
|
with open(os.path.join(dst, "tkg_config_mainly.properties"), 'rb') as f:
|
||||||
|
self.context.config = javaproperties.load(f)
|
||||||
|
|
||||||
|
# 不打admob
|
||||||
|
if self.context.admob_app_id is None or self.context.admob_app_id == "":
|
||||||
|
self.context.admob_app_id = self.context.get_config("admob_id")
|
||||||
|
return
|
||||||
|
|
||||||
|
if os.path.exists(dst):
|
||||||
|
shutil.rmtree(dst)
|
||||||
|
|
||||||
|
result = FileUtils.decompress(target_path, dst)
|
||||||
|
app_logger().debug(f"{target_path} -> {dst} , 解压结果: {result}")
|
||||||
|
|
||||||
|
mainly_path = os.path.join(dst, "mainly")
|
||||||
|
if not os.path.exists(mainly_path):
|
||||||
|
mainly_path = os.path.join(dst, "appConfig")
|
||||||
|
|
||||||
|
google_services_json_path = os.path.join(dst, "google-services.json")
|
||||||
|
if not os.path.exists(google_services_json_path):
|
||||||
|
google_services_json_path = os.path.join(dst, "appConfig", "google-services.json")
|
||||||
|
|
||||||
|
FileUtils.copy(google_services_json_path,
|
||||||
|
os.path.join(self.context.temp_project_path, "google-services.json"),
|
||||||
|
True)
|
||||||
|
|
||||||
|
dst_path = os.path.join(self.context.temp_project_path, f"launcher-game{os.sep}assets")
|
||||||
|
|
||||||
|
for file in list(filter(lambda f: not (f == "google_fonts.json" or f == "pag_gl_slide.pag"),
|
||||||
|
os.listdir(dst_path))):
|
||||||
|
FileUtils.delete(os.path.join(dst_path, file), True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
for file in list(filter(lambda f: f.find(".") <= 0, os.listdir(mainly_path))):
|
||||||
|
FileUtils.copy(os.path.join(mainly_path, file), os.path.join(dst_path, file), True)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_config()
|
||||||
|
self.context.save_cache_config("config", self.context.config_config_md5)
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
from scripts.task import Task
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
GAME_ACTIVITY_PATH = f"launcher-game/src/com/game/launcher/activity/GLGameActivity.kt".replace("/", os.sep)
|
||||||
|
ANDROID_MANIFEST_PATH = f"launcher-game/AndroidManifest.xml".replace("/", os.sep)
|
||||||
|
|
||||||
|
|
||||||
|
def process_manifest(input_path, output_path):
|
||||||
|
# 解析XML文件
|
||||||
|
tree = ET.parse(input_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# 定义命名空间映射
|
||||||
|
android_namespace = 'http://schemas.android.com/apk/res/android'
|
||||||
|
ET.register_namespace('android', android_namespace)
|
||||||
|
namespaces = {'android': android_namespace}
|
||||||
|
|
||||||
|
# 处理application标签,移除所有属性
|
||||||
|
application = root.find('application')
|
||||||
|
if application is not None:
|
||||||
|
# 保存所有子节点
|
||||||
|
children = list(application)
|
||||||
|
# 清除application标签
|
||||||
|
root.remove(application)
|
||||||
|
# 创建新的application标签(无属性)
|
||||||
|
new_application = ET.Element('application')
|
||||||
|
# 添加回所有子节点
|
||||||
|
for child in children:
|
||||||
|
new_application.append(child)
|
||||||
|
# 将新的application标签添加回root
|
||||||
|
root.append(new_application)
|
||||||
|
|
||||||
|
# 查找并删除指定的activity节点
|
||||||
|
activity_to_remove = new_application.find(
|
||||||
|
".//activity[@android:name='com.unity3d.player.UnityPlayerActivity']",
|
||||||
|
namespaces=namespaces
|
||||||
|
)
|
||||||
|
if activity_to_remove is not None:
|
||||||
|
new_application.remove(activity_to_remove)
|
||||||
|
|
||||||
|
# 保存处理后的XML,保留android命名空间前缀
|
||||||
|
rough_string = ET.tostring(root, 'utf-8')
|
||||||
|
reparsed = minidom.parseString(rough_string)
|
||||||
|
pretty_xml = reparsed.toprettyxml(indent=" ", encoding='utf-8')
|
||||||
|
|
||||||
|
# 去除空行
|
||||||
|
lines = pretty_xml.decode('utf-8').split('\n')
|
||||||
|
non_empty_lines = [line for line in lines if line.strip() != '']
|
||||||
|
pretty_xml = '\n'.join(non_empty_lines).encode('utf-8')
|
||||||
|
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(pretty_xml)
|
||||||
|
|
||||||
|
print(f"处理完成,结果已保存到 {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def uncomment_line(content, line_pattern):
|
||||||
|
"""
|
||||||
|
取消指定行的注释
|
||||||
|
|
||||||
|
:param content: 文件内容
|
||||||
|
:param line_pattern: 要取消注释的行内容(不含前导//和空格)
|
||||||
|
:return: 更新后的内容
|
||||||
|
"""
|
||||||
|
# 匹配以//开头,后跟任意空格,然后是目标行内容
|
||||||
|
pattern = rf'^(\s*)//\s*({re.escape(line_pattern)}\s*)$'
|
||||||
|
|
||||||
|
# 替换为去注释版本(保留原有缩进)
|
||||||
|
replacement = rf'\1\2'
|
||||||
|
|
||||||
|
updated_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
|
||||||
|
return updated_content
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateGameRes(Task):
|
||||||
|
|
||||||
|
def update_game_result(self):
|
||||||
|
if not self.context.update_res_unity:
|
||||||
|
app_logger().info("No update game res found")
|
||||||
|
return
|
||||||
|
"""
|
||||||
|
更新游戏资源
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if self.context.game_type == "unity_native":
|
||||||
|
res_path = self.context.res_unity_path
|
||||||
|
dst = os.path.join(self.context.temp_project_path, "unityLibrary")
|
||||||
|
temp_dst = dst + "_res"
|
||||||
|
if os.path.exists(dst):
|
||||||
|
result = FileUtils.delete(dst, True)
|
||||||
|
app_logger().info(f"删除unityLibrary结果 : {result}")
|
||||||
|
|
||||||
|
if os.path.exists(temp_dst):
|
||||||
|
result = FileUtils.delete(temp_dst, True)
|
||||||
|
app_logger().info(f"删除temp unityLibrary结果 : {result}")
|
||||||
|
|
||||||
|
FileUtils.decompress(res_path, temp_dst)
|
||||||
|
|
||||||
|
build_path = os.path.join(temp_dst, "build")
|
||||||
|
if os.path.exists(build_path):
|
||||||
|
FileUtils.delete(build_path, True)
|
||||||
|
|
||||||
|
if os.listdir(temp_dst).index("unityLibrary") >= 0:
|
||||||
|
FileUtils.copy(os.path.join(temp_dst, "unityLibrary"), dst)
|
||||||
|
else:
|
||||||
|
FileUtils.copy(temp_dst, dst)
|
||||||
|
|
||||||
|
android_manifest_xml_path = os.path.join(dst, "src", "main", "AndroidManifest.xml")
|
||||||
|
process_manifest(android_manifest_xml_path, android_manifest_xml_path)
|
||||||
|
|
||||||
|
text = open(os.path.join(dst, "build.gradle"), "r", encoding="utf-8").read()
|
||||||
|
text = text.replace("implementation", "api")
|
||||||
|
if not 'namespace "com.unity3d.player"' in text:
|
||||||
|
text = text.replace("compileSdkVersion", """
|
||||||
|
namespace "com.unity3d.player"
|
||||||
|
compileSdkVersion""")
|
||||||
|
|
||||||
|
text = text.replace("unityStreamingAssets.tokenize(', ')",
|
||||||
|
'[".unity3d", ".bundle", ".version", ".bytes", ".hash"]')
|
||||||
|
text = text.replace("apply plugin: 'com.android.library'", """
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
open(os.path.join(dst, "build.gradle"), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
|
lines = open(os.path.join(dst, "build.gradle"), "r", encoding="utf-8").readlines()
|
||||||
|
new_lines = []
|
||||||
|
for line in lines:
|
||||||
|
if line.find("com.game:hachisdk") > 0:
|
||||||
|
continue
|
||||||
|
new_lines.append(line)
|
||||||
|
open(os.path.join(dst, "build.gradle"), "w", encoding="utf-8").writelines(new_lines)
|
||||||
|
|
||||||
|
# 引用Unity项目
|
||||||
|
text = open(os.path.join(self.context.temp_project_path, "ad.gradle"), "r", encoding="utf-8").read()
|
||||||
|
text = uncomment_line(text, "implementation projects.unityLibrary")
|
||||||
|
open(os.path.join(self.context.temp_project_path, "ad.gradle"), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
|
text = open(os.path.join(self.context.temp_project_path, "settings.gradle"), "r", encoding="utf-8").read()
|
||||||
|
text = uncomment_line(text, "include ':unityLibrary'")
|
||||||
|
open(os.path.join(self.context.temp_project_path, "settings.gradle"), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
|
# launcher 引用 unityActivity
|
||||||
|
text = open(os.path.join(self.context.temp_project_path, GAME_ACTIVITY_PATH), "r", encoding="utf-8").read()
|
||||||
|
text = text.replace("GLGameWebActivity", "com.unity3d.player.UnityPlayerActivity")
|
||||||
|
open(os.path.join(self.context.temp_project_path, GAME_ACTIVITY_PATH), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
|
text = open(os.path.join(self.context.temp_project_path, ANDROID_MANIFEST_PATH), "r",
|
||||||
|
encoding="utf-8").read()
|
||||||
|
text = text.replace("@style/LauncherGameIntroTheme", "@style/UnityThemeSelector")
|
||||||
|
open(os.path.join(self.context.temp_project_path, ANDROID_MANIFEST_PATH), "w", encoding="utf-8").write(text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception(f"不支持的游戏类型 : {self.context.game_type}")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_game_result()
|
||||||
|
|
||||||
|
self.context.save_cache_config("res_unity", self.context.config_res_unity_md5)
|
||||||
|
pass
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from scripts.task import Task
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateIcon(Task):
|
||||||
|
|
||||||
|
def update_icon(self):
|
||||||
|
"""
|
||||||
|
更新游戏Icon
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.context.update_res_icon:
|
||||||
|
app_logger().info("No update res icon found")
|
||||||
|
return
|
||||||
|
tag = "res_icon_resources"
|
||||||
|
dst = os.path.join(self.context.temp_project_path, tag)
|
||||||
|
if os.path.exists(dst):
|
||||||
|
shutil.rmtree(dst)
|
||||||
|
|
||||||
|
FileUtils.decompress(self.context.res_icon_path, dst)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(dst):
|
||||||
|
for file in files:
|
||||||
|
temp_tart_path = os.path.join(root, file)
|
||||||
|
if temp_tart_path.find("__MACOSX") > 0:
|
||||||
|
continue
|
||||||
|
temp_dst = temp_tart_path.replace(tag, "launcher-game" + os.sep + "res")
|
||||||
|
app_logger().debug(f"copy icon = {temp_tart_path} -> {temp_dst}")
|
||||||
|
FileUtils.copy(temp_tart_path, temp_dst, True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_icon()
|
||||||
|
self.context.save_cache_config("res_icon", self.context.config_res_icon_md5)
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from scripts.task import Task
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateImage(Task):
|
||||||
|
|
||||||
|
def update_image(self):
|
||||||
|
"""
|
||||||
|
更新游戏的资源
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.context.update_res_img:
|
||||||
|
app_logger().info("No update image found")
|
||||||
|
return
|
||||||
|
dst = os.path.join(self.context.temp_project_path, "drawable_res")
|
||||||
|
if os.path.exists(dst):
|
||||||
|
shutil.rmtree(dst)
|
||||||
|
|
||||||
|
FileUtils.decompress(self.context.res_img_path, dst)
|
||||||
|
|
||||||
|
if os.path.join(dst, "drawable-xxhdpi"):
|
||||||
|
dst = os.path.join(dst, "drawable-xxhdpi")
|
||||||
|
|
||||||
|
target_root_path = os.path.join(self.context.temp_project_path,
|
||||||
|
f"launcher-game{os.sep}res{os.sep}drawable-xxhdpi")
|
||||||
|
|
||||||
|
image_list = list(map(lambda f: Path(f).stem, os.listdir(target_root_path)))
|
||||||
|
|
||||||
|
for file in os.listdir(dst):
|
||||||
|
if file == ".DS_Store":
|
||||||
|
continue
|
||||||
|
res_name = Path(file).stem
|
||||||
|
file_name = self.context.get_map_from_key(res_name)
|
||||||
|
temp_tar = os.path.join(dst, file)
|
||||||
|
temp_dst = os.path.join(target_root_path, file).replace(file_name, self.context.get_map_from_key(res_name))
|
||||||
|
if file_name in image_list:
|
||||||
|
FileUtils.delete(os.path.join(target_root_path, file_name + ".png"))
|
||||||
|
FileUtils.delete(os.path.join(target_root_path, file_name + ".jpg"))
|
||||||
|
pass
|
||||||
|
app_logger().info(f"copy {temp_tar} => {temp_dst}")
|
||||||
|
FileUtils.copy(temp_tar, temp_dst, True)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_image()
|
||||||
|
self.context.save_cache_config("res_img", self.context.config_res_img_md5)
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from scripts.task import Task
|
||||||
|
from utils import FileUtils
|
||||||
|
from utils.logger_utils import app_logger
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectUpdateKeystore(Task):
|
||||||
|
|
||||||
|
def update_keystore(self):
|
||||||
|
if not self.context.update_keystore:
|
||||||
|
app_logger().info("No update_keystore")
|
||||||
|
return
|
||||||
|
|
||||||
|
name = os.path.basename(self.context.keystore_path).replace(".keystore", "")
|
||||||
|
target_path = os.path.join(self.context.temp_project_path, name + ".keystore")
|
||||||
|
|
||||||
|
if os.path.exists(target_path):
|
||||||
|
os.remove(target_path)
|
||||||
|
|
||||||
|
result = FileUtils.copy(self.context.keystore_path, target_path)
|
||||||
|
|
||||||
|
app_logger().debug(f"copy keystore result {result}")
|
||||||
|
|
||||||
|
target_path = os.path.join(self.context.temp_project_path, "keystore.properties")
|
||||||
|
if os.path.exists(target_path):
|
||||||
|
os.remove(target_path)
|
||||||
|
|
||||||
|
open(target_path, "w", encoding="utf-8").write(
|
||||||
|
f"""
|
||||||
|
keyAlias={name}
|
||||||
|
keyPassword=123456
|
||||||
|
storeFile=./{name}.keystore
|
||||||
|
storePassword=123456
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_keystore()
|
||||||
|
self.context.save_cache_config("keystore", self.context.config_keystore_md5)
|
||||||
|
pass
|
||||||
|
|
@ -5,7 +5,6 @@ class Task:
|
||||||
|
|
||||||
def __init__(self, context: Context):
|
def __init__(self, context: Context):
|
||||||
self.context = context
|
self.context = context
|
||||||
pass
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue