Various changes, block telemetry feature.
-Sp/--patch is now required to do block telemetry before patching. Still preparing for hdiffpatch (will be coming at 1.10) Ay yo hosty support coming soon xD
This commit is contained in:
parent
dbf6cf6d21
commit
aaf728445d
2
setup.py
2
setup.py
@ -9,7 +9,7 @@ README = (HERE / "README.md").read_text()
|
||||
|
||||
setup(
|
||||
name='worthless',
|
||||
version='1.2.8-1',
|
||||
version='1.2.9',
|
||||
packages=['worthless', 'worthless.classes', 'worthless.classes.launcher', 'worthless.classes.installer'],
|
||||
url='https://git.froggi.es/tretrauit/worthless-launcher',
|
||||
license='MIT License',
|
||||
|
@ -2,11 +2,22 @@ APP_NAME="worthless"
|
||||
APP_AUTHOR="tretrauit"
|
||||
LAUNCHER_API_URL_OS = "https://sdk-os-static.hoyoverse.com/hk4e_global/mdk/launcher/api"
|
||||
LAUNCHER_API_URL_CN = "https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api"
|
||||
HDIFFPATCH_GIT_URL="https://github.com/sisong/HDiffPatch"
|
||||
PATCH_GIT_URL = "https://notabug.org/Krock/dawn"
|
||||
TELEMETRY_URL_LIST = [
|
||||
"log-upload-os.mihoyo.com",
|
||||
"log-upload-eur.mihoyo.com",
|
||||
"log-upload-os.hoyoverse.com",
|
||||
"log-upload.mihoyo.com",
|
||||
"overseauspider.yuanshen.com"
|
||||
]
|
||||
TELEMETRY_URL_CN_LIST = [
|
||||
"log-upload.mihoyo.com",
|
||||
"uspider.yuanshen.com"
|
||||
]
|
||||
TELEMETRY_OPTIONAL_URL_LIST = [
|
||||
"prd-lender.cdp.internal.unity3d.com",
|
||||
"thind-prd-knob.data.ie.unity3d.com",
|
||||
"thind-gke-usc.prd.data.corp.unity3d.com",
|
||||
"cdp.cloud.unity3d.com",
|
||||
"remote-config-proxy-prd.uca.cloud.unity3d.com"
|
||||
]
|
@ -37,6 +37,24 @@ class UI:
|
||||
def get_game_version(self):
|
||||
print(self._installer.get_game_version())
|
||||
|
||||
def block_telemetry(self):
|
||||
print("Checking for available telemetry to block...")
|
||||
try:
|
||||
asyncio.run(self._patcher.block_telemetry())
|
||||
except ValueError:
|
||||
print("No telemetry to block.")
|
||||
else:
|
||||
print("Telemetry blocked.")
|
||||
|
||||
def check_telemetry(self):
|
||||
block_status = asyncio.run(self._patcher.is_telemetry_blocked())
|
||||
if not block_status:
|
||||
print("Telemetry is blocked.")
|
||||
else:
|
||||
print("Telemetry is not blocked, you need to block these hosts below.")
|
||||
for block in block_status:
|
||||
print(block)
|
||||
|
||||
def _update_from_archive(self, filepath):
|
||||
print("Reverting patches if patched...")
|
||||
self._patcher.revert_patch(True)
|
||||
@ -72,11 +90,13 @@ class UI:
|
||||
if not self._ask("Do you want to patch the game? (This will overwrite your game files!)"):
|
||||
print("Aborting patch process.")
|
||||
return
|
||||
print("Patching game...")
|
||||
self.block_telemetry()
|
||||
print("Updating patches...")
|
||||
asyncio.run(self._patcher.download_patch())
|
||||
print("Patching game...")
|
||||
self._patcher.apply_patch(login_fix)
|
||||
print("Game patched.")
|
||||
print("Please refrain from sharing this project to public especially official channels, thank you.")
|
||||
print("Please refrain from sharing this project to public, thank you.")
|
||||
|
||||
def install_from_file(self, filepath):
|
||||
gamever = self._installer.get_game_version()
|
||||
@ -96,6 +116,10 @@ class UI:
|
||||
self._install_from_archive(filepath, False)
|
||||
print("Game installed successfully.")
|
||||
|
||||
def download_patch(self):
|
||||
print("Downloading patches...")
|
||||
asyncio.run(self._patcher.download_patch())
|
||||
|
||||
def download_game(self):
|
||||
print("Downloading full game (This will take a long time)...")
|
||||
asyncio.run(self._installer.download_full_game())
|
||||
@ -235,6 +259,7 @@ def main():
|
||||
parser.add_argument("-Rv", "--remove-voiceover", action="store_true", help="Remove a Voiceover pack (if installed)")
|
||||
parser.add_argument("--get-game-version", action="store_true", help="Get the current game version")
|
||||
parser.add_argument("--no-overseas", action="store_true", help="Don't use overseas server")
|
||||
parser.add_argument("--check-telemetry", action="store_true", help="Check for the telemetry information")
|
||||
parser.add_argument("--from-ver", action="store", help="Override the detected game version", type=str, default=None)
|
||||
parser.add_argument("--noconfirm", action="store_true",
|
||||
help="Do not ask any for confirmation. (Ignored in interactive mode)")
|
||||
@ -243,7 +268,8 @@ def main():
|
||||
args.remove and not args.remove_patch and not args.remove_voiceover and not args.get_game_version and not \
|
||||
args.install_voiceover_from_file and not args.update_voiceover and not args.download_game and not \
|
||||
args.download_voiceover and not args.download_game_update and not args.download_voiceover_update and not \
|
||||
args.install_voiceover_from_file and not args.update_all and not args.login_fix
|
||||
args.install_voiceover_from_file and not args.update_all and not args.login_fix and not args.check_telemetry\
|
||||
and not args.from_ver
|
||||
if args.temporary_dir:
|
||||
args.temporary_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@ -261,9 +287,15 @@ def main():
|
||||
if args.install_from_file and args.install:
|
||||
raise ValueError("Cannot specify both --install-from-file and --install arguments.")
|
||||
|
||||
if args.from_ver:
|
||||
ui.override_game_version(args.from_ver)
|
||||
|
||||
if args.get_game_version:
|
||||
ui.get_game_version()
|
||||
|
||||
if args.check_telemetry:
|
||||
ui.check_telemetry()
|
||||
|
||||
if args.download_game:
|
||||
ui.download_game()
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import re
|
||||
import shutil
|
||||
|
||||
import platform
|
||||
import aiohttp
|
||||
import appdirs
|
||||
import zipfile
|
||||
@ -13,6 +13,86 @@ from worthless import constants
|
||||
from worthless.launcher import Launcher
|
||||
|
||||
|
||||
async def _download_file(file_url: str, file_name: str, file_path: Path | str, file_len: int = None, overwrite=False, chunks=8192):
|
||||
"""
|
||||
Download file name to temporary directory,
|
||||
:param file_url:
|
||||
:param file_name:
|
||||
:return:
|
||||
"""
|
||||
params = {}
|
||||
file_path = AsyncPath(file_path).joinpath(file_name)
|
||||
if overwrite:
|
||||
await file_path.unlink(missing_ok=True)
|
||||
if await file_path.exists():
|
||||
cur_len = len(await file_path.read_bytes())
|
||||
params |= {
|
||||
"Range": f"bytes={cur_len}-{file_len if file_len else ''}"
|
||||
}
|
||||
else:
|
||||
await file_path.touch()
|
||||
async with aiohttp.ClientSession() as session:
|
||||
rsp = await session.get(file_url, params=params, timeout=None)
|
||||
rsp.raise_for_status()
|
||||
while True:
|
||||
chunk = await rsp.content.read(chunks)
|
||||
if not chunk:
|
||||
break
|
||||
async with file_path.open("ab") as f:
|
||||
await f.write(chunk)
|
||||
|
||||
|
||||
class HDiffPatch:
|
||||
def __init__(self, git_url=None, data_dir=None):
|
||||
if not git_url:
|
||||
repo_url = constants.HDIFFPATCH_GIT_URL
|
||||
self._git_url = git_url
|
||||
if not data_dir:
|
||||
self._appdirs = appdirs.AppDirs(constants.APP_NAME, constants.APP_AUTHOR)
|
||||
self.temp_path = Path(self._appdirs.user_cache_dir).joinpath("Tools/HDiffPatch")
|
||||
else:
|
||||
if not isinstance(data_dir, Path):
|
||||
data_dir = Path(data_dir)
|
||||
self.temp_path = data_dir.joinpath("Temp/Tools/HDiffPatch")
|
||||
self.temp_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@staticmethod
|
||||
def _get_platform_arch():
|
||||
match platform.system():
|
||||
case "Windows":
|
||||
match platform.architecture()[0]:
|
||||
case "32bit":
|
||||
return "windows32"
|
||||
case "64bit":
|
||||
return "windows64"
|
||||
case "Linux":
|
||||
match platform.architecture()[0]:
|
||||
case "32bit":
|
||||
return "linux32"
|
||||
case "64bit":
|
||||
return "linux64"
|
||||
case "Darwin":
|
||||
return "macos"
|
||||
|
||||
raise RuntimeError("Unsupported platform")
|
||||
|
||||
def _get_hdiffpatch_exec(self, exec_name):
|
||||
if shutil.which(exec_name):
|
||||
return exec_name
|
||||
if not any(self.temp_path.iterdir()):
|
||||
return None
|
||||
platform_arch_path = self.temp_path.joinpath(self._get_platform_arch())
|
||||
if platform_arch_path.joinpath(exec_name).exists():
|
||||
return str(platform_arch_path.joinpath(exec_name))
|
||||
return None
|
||||
|
||||
def get_hpatchz_executable(self):
|
||||
return self._get_hdiffpatch_exec("hpatchz")
|
||||
|
||||
def get_hdiffz_executable(self):
|
||||
return self._get_hdiffpatch_exec("hdiffz")
|
||||
|
||||
|
||||
class Installer:
|
||||
def _read_version_from_config(self):
|
||||
warnings.warn("This function is not reliable as upgrading game version from worthless\
|
||||
@ -88,6 +168,7 @@ class Installer:
|
||||
self._version = None
|
||||
self._overseas = overseas
|
||||
self._launcher = Launcher(self._gamedir, overseas=self._overseas)
|
||||
self._hdiffpatch = HDiffPatch(data_dir=data_dir)
|
||||
self._version = self.get_game_version()
|
||||
|
||||
def set_download_chunk(self, chunk: int):
|
||||
@ -100,26 +181,7 @@ class Installer:
|
||||
:param file_name:
|
||||
:return:
|
||||
"""
|
||||
params = {}
|
||||
file_path = AsyncPath(self.temp_path).joinpath(file_name)
|
||||
if overwrite:
|
||||
await file_path.unlink(missing_ok=True)
|
||||
if await file_path.exists():
|
||||
cur_len = len(await file_path.read_bytes())
|
||||
params |= {
|
||||
"Range": f"bytes={cur_len}-{file_len if file_len else ''}"
|
||||
}
|
||||
else:
|
||||
await file_path.touch()
|
||||
async with aiohttp.ClientSession() as session:
|
||||
rsp = await session.get(file_url, params=params, timeout=None)
|
||||
rsp.raise_for_status()
|
||||
while True:
|
||||
chunk = await rsp.content.read(self._download_chunk)
|
||||
if not chunk:
|
||||
break
|
||||
async with file_path.open("ab") as f:
|
||||
await f.write(chunk)
|
||||
await _download_file(file_url, file_name, self.temp_path, file_len=file_len, overwrite=overwrite)
|
||||
|
||||
def get_game_archive_version(self, game_archive: str | Path):
|
||||
if not game_archive.exists():
|
||||
|
35
worthless/linux.py
Normal file
35
worthless/linux.py
Normal file
@ -0,0 +1,35 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class LinuxUtils:
|
||||
"""Utilities for Linux-specific tasks.
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
async def _exec_command(self, *args):
|
||||
"""Execute a command using pkexec (friendly gui)
|
||||
"""
|
||||
rsp = await asyncio.create_subprocess_exec(*args)
|
||||
match rsp.returncode:
|
||||
case 127:
|
||||
raise OSError("Authentication failed.")
|
||||
case 128:
|
||||
raise RuntimeError("User cancelled the authentication.")
|
||||
|
||||
return rsp
|
||||
|
||||
async def write_text_to_file(self, text, file_path: str | Path):
|
||||
"""Write text to a file using pkexec (friendly gui)
|
||||
"""
|
||||
if isinstance(file_path, Path):
|
||||
file_path = str(file_path)
|
||||
await self._exec_command('pkexec', 'echo', text, '>', file_path)
|
||||
|
||||
async def append_text_to_file(self, text, file_path: str | Path):
|
||||
"""Append text to a file using pkexec (friendly gui)
|
||||
"""
|
||||
if isinstance(file_path, Path):
|
||||
file_path = str(file_path)
|
||||
await self._exec_command('pkexec', 'echo', text, '>>', file_path)
|
@ -6,6 +6,7 @@ from pathlib import Path
|
||||
import shutil
|
||||
import aiohttp
|
||||
import asyncio
|
||||
from worthless import linux
|
||||
from worthless import constants
|
||||
from worthless.launcher import Launcher
|
||||
from worthless.installer import Installer
|
||||
@ -31,8 +32,11 @@ class Patcher:
|
||||
self._patch_path = data_dir.joinpath("Patch")
|
||||
self._temp_path = data_dir.joinpath("Temp/Patcher")
|
||||
self._overseas = overseas
|
||||
self._installer = Installer(self._gamedir, overseas=overseas, data_dir=self._temp_path)
|
||||
self._installer = Installer(self._gamedir, overseas=overseas, data_dir=data_dir)
|
||||
self._launcher = Launcher(self._gamedir, overseas=overseas)
|
||||
match platform.system():
|
||||
case "Linux":
|
||||
self._linuxutils = linux.LinuxUtils()
|
||||
|
||||
@staticmethod
|
||||
async def _get(url, **kwargs) -> aiohttp.ClientResponse:
|
||||
@ -100,6 +104,40 @@ class Patcher:
|
||||
"""
|
||||
await self._download_repo()
|
||||
|
||||
async def is_telemetry_blocked(self):
|
||||
"""
|
||||
Check if the telemetry is blocked.
|
||||
|
||||
"""
|
||||
if self._overseas:
|
||||
telemetry_url = constants.TELEMETRY_URL_LIST
|
||||
else:
|
||||
telemetry_url = constants.TELEMETRY_URL_CN_LIST
|
||||
unblocked_list = []
|
||||
async with aiohttp.ClientSession() as session:
|
||||
for url in telemetry_url:
|
||||
try:
|
||||
await session.get("https://" + url)
|
||||
except (aiohttp.ClientResponseError, aiohttp.ClientConnectorError):
|
||||
continue
|
||||
else:
|
||||
unblocked_list.append(url)
|
||||
return None if unblocked_list == [] else unblocked_list
|
||||
|
||||
async def block_telemetry(self):
|
||||
telemetry = await self.is_telemetry_blocked()
|
||||
if not telemetry:
|
||||
raise ValueError("All telemetry are blocked")
|
||||
telemetry_hosts = ""
|
||||
for url in telemetry:
|
||||
telemetry_hosts += "0.0.0.0 " + url + "\n"
|
||||
match platform.system():
|
||||
case "Linux":
|
||||
await self._linuxutils.append_text_to_file(telemetry_hosts)
|
||||
return
|
||||
# TODO: Windows and macOS
|
||||
raise NotImplementedError("Platform not implemented.")
|
||||
|
||||
async def _patch_unityplayer_fallback(self):
|
||||
# xdelta3-python doesn't work becuase it's outdated.
|
||||
if self._overseas:
|
||||
@ -115,7 +153,10 @@ class Patcher:
|
||||
|
||||
async def _patch_xlua_fallback(self):
|
||||
# xdelta3-python doesn't work becuase it's outdated.
|
||||
patch = "xlua_patch.vcdiff"
|
||||
if self._overseas:
|
||||
patch = "unityplayer_patch_os.vcdiff"
|
||||
else:
|
||||
patch = "unityplayer_patch_cn.vcdiff"
|
||||
gamever = "".join(self._installer.get_game_version().split("."))
|
||||
data_name = self._installer.get_game_data_name()
|
||||
xlua_path = self._gamedir.joinpath("{}/Plugins/xlua.dll".format(data_name))
|
||||
|
Loading…
Reference in New Issue
Block a user