diff --git a/MCSL2.py b/MCSL2.py index d74038a..48f6647 100644 --- a/MCSL2.py +++ b/MCSL2.py @@ -14,12 +14,14 @@ Main entry. """ +import os import sys + from PyQt5.QtCore import Qt, QLocale, QObject, QEvent from PyQt5.QtWidgets import QApplication + # from viztracer import VizTracer -from MCSL2Lib.utils import MCSL2Logger class MCSL2Application(QApplication): @@ -36,6 +38,12 @@ def notify(self, a0: QObject, a1: QEvent) -> bool: if __name__ == "__main__": + # Override cwd + os.chdir(os.path.dirname(os.path.abspath(__file__))) + + # Import after overriding cwd to export the logs to the correct place + from MCSL2Lib.utils import MCSL2Logger + # Debug # tracer = VizTracer() # tracer.enable_thread_tracing() @@ -58,10 +66,10 @@ def notify(self, a0: QObject, a1: QEvent) -> bool: from MCSL2Lib.variables import GlobalMCSL2Variables if ( - cfg.get(cfg.oldExecuteable) == "python" - or cfg.get(cfg.oldExecuteable) == "python.exe" - or cfg.get(cfg.oldExecuteable) == "py" - or cfg.get(cfg.oldExecuteable) == "py.exe" + cfg.get(cfg.oldExecuteable) == "python" + or cfg.get(cfg.oldExecuteable) == "python.exe" + or cfg.get(cfg.oldExecuteable) == "py" + or cfg.get(cfg.oldExecuteable) == "py.exe" ): GlobalMCSL2Variables.devMode = True else: diff --git a/MCSL2Lib/Pages/settingsPage.py b/MCSL2Lib/Pages/settingsPage.py index cc9a57a..26e6d05 100644 --- a/MCSL2Lib/Pages/settingsPage.py +++ b/MCSL2Lib/Pages/settingsPage.py @@ -13,7 +13,8 @@ """ Settings page. """ - +import os +import platform from datetime import datetime from PyQt5.QtCore import QSize, Qt, QRect, pyqtSignal, pyqtSlot @@ -49,7 +50,7 @@ setThemeColor, ) -from MCSL2Lib import MCSL2VERSION +from MCSL2Lib import MCSL2VERSION, utils from MCSL2Lib.ProgramControllers.aria2ClientController import ( Aria2BootThread, Aria2Controller, @@ -335,14 +336,18 @@ def __init__(self, parent=None): self.startOnStartup = SwitchSettingCard( icon=FIF.POWER_BUTTON, title=self.tr("开机自启动"), - content=self.tr("好像还做不到啊。"), + content=self.tr("仅限 Windows。"), configItem=cfg.startOnStartup, parent=self.consoleSettingsGroup, ) self.alwaysRunAsAdministrator.setEnabled(False) - self.startOnStartup.setEnabled(False) + if platform.system() == 'Linux' or 'Windows': + self.startOnStartup.setEnabled(True) + else: + self.startOnStartup.setEnabled(False) self.themeColor.colorChanged.connect(lambda cl: setThemeColor(color=cl, lazy=True)) self.themeMode.optionChanged.connect(lambda ci: setTheme(cfg.get(ci), lazy=True)) + self.startOnStartup.checkedChanged.connect(self.setStartup) # self.themeMode.optionChanged.connect(self.showNeedRestartMsg) self.programSettingsGroup.addSettingCard(self.themeMode) self.programSettingsGroup.addSettingCard(self.themeColor) @@ -520,7 +525,8 @@ def __init__(self, parent=None): self.subTitleLabel.setText(self.tr("自定义你的 MCSL2。")) self.aboutContent.setText( self.tr( - "MCServerLauncher 2 是一个开源非营利性项目,遵循 GNU General Public License 3.0 开源协议。\n任何人皆可使用 MCSL2 的源码进行再编译、修改以及发行,\n但必须在相关源代码中以及软件中给出声明,并且二次分发版本的项目名称应与 “MCSL2” 有明显辨识度。\n“MCServerLauncher 2 软件” 已进行中华人民共和国计算机软件著作权登记,一切侵权行为将依法追究。\n计算机软件著作权登记号: 2024SR0343868\n\n© 2022 - 2024 MCSL开发组 保留所有权利。\n" # noqa : E501 + "MCServerLauncher 2 是一个开源非营利性项目,遵循 GNU General Public License 3.0 开源协议。\n任何人皆可使用 MCSL2 的源码进行再编译、修改以及发行,\n但必须在相关源代码中以及软件中给出声明,并且二次分发版本的项目名称应与 “MCSL2” 有明显辨识度。\n“MCServerLauncher 2 软件” 已进行中华人民共和国计算机软件著作权登记,一切侵权行为将依法追究。\n计算机软件著作权登记号: 2024SR0343868\n\n© 2022 - 2024 MCSL开发组 保留所有权利。\n" + # noqa : E501 ) ) self.aboutTitle.setText(self.tr("关于")) @@ -663,10 +669,10 @@ def showUpdateMsg(self, latestVerInfo): def generateSystemReport(self): """创建系统报告""" report = ( - self.tr("生成时间: ") - + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) - + "\n" - + genSysReport() + self.tr("生成时间: ") + + str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + + "\n" + + genSysReport() ) title = self.tr("MCServerLauncher 2 系统报告") @@ -690,3 +696,10 @@ def generateSystemReport(self): ) ) w.exec() + + @staticmethod + def setStartup(state: bool): + if state: + utils.setStartOnStartup() + else: + utils.removeStartOnStartup() diff --git a/MCSL2Lib/ServerControllers/forge/installer/json/artifact.py b/MCSL2Lib/ServerControllers/forge/installer/json/artifact.py index 90025b1..550ed68 100644 --- a/MCSL2Lib/ServerControllers/forge/installer/json/artifact.py +++ b/MCSL2Lib/ServerControllers/forge/installer/json/artifact.py @@ -30,7 +30,7 @@ def from_(descriptor: str) -> "Artifact": idx = pts[last].find("@") if idx != -1: - ext = pts[last][idx + 1 :] + ext = pts[last][idx + 1:] pts[last] = pts[last][:idx] else: ext = "jar" diff --git a/MCSL2Lib/ServerControllers/windowCreator.py b/MCSL2Lib/ServerControllers/windowCreator.py index 4111f23..a6e75da 100644 --- a/MCSL2Lib/ServerControllers/windowCreator.py +++ b/MCSL2Lib/ServerControllers/windowCreator.py @@ -1332,7 +1332,7 @@ def getKnownServerPlayers(self) -> str: def initQuickMenu_Difficulty(self): """快捷菜单-服务器游戏难度""" - textDiffiultyList = ["peaceful", "easy", "normal", "hard"] + textDifficultyList = ["peaceful", "easy", "normal", "hard"] if self.getRunningStatus(): try: self.difficulty.setCurrentIndex( @@ -1340,7 +1340,7 @@ def initQuickMenu_Difficulty(self): ) except ValueError: self.difficulty.setCurrentIndex( - int(textDiffiultyList.index(self.serverConfig.serverProperties["difficulty"])) + int(textDifficultyList.index(self.serverConfig.serverProperties["difficulty"])) ) except Exception: pass @@ -1349,8 +1349,8 @@ def initQuickMenu_Difficulty(self): self.showServerNotOpenMsg() def runQuickMenu_Difficulty(self): - textDiffiultyList = ["peaceful", "easy", "normal", "hard"] - self.sendCommand(f"difficulty {textDiffiultyList[self.difficulty.currentIndex()]}") + textDifficultyList = ["peaceful", "easy", "normal", "hard"] + self.sendCommand(f"difficulty {textDifficultyList[self.difficulty.currentIndex()]}") def initQuickMenu_GameMode(self): """快捷菜单-游戏模式""" diff --git a/MCSL2Lib/utils.py b/MCSL2Lib/utils.py index 3b4e0e1..bacf2f0 100644 --- a/MCSL2Lib/utils.py +++ b/MCSL2Lib/utils.py @@ -18,6 +18,10 @@ import functools import hashlib import inspect +import os.path +import platform +import subprocess +import sys from json import dumps, loads from os import makedirs, path as osp from types import TracebackType @@ -28,7 +32,7 @@ import requests from PyQt5.QtCore import QUrl, QThread, QThreadPool, QFile from PyQt5.QtGui import QDesktopServices - +from platform import system from MCSL2Lib.ProgramControllers.logController import _MCSL2Logger MCSL2Logger = _MCSL2Logger() @@ -235,15 +239,12 @@ def checkSHA1( 检查文件的SHA1值是否正确 """ rv = [] - if _filter is None: - def _filter(a, b): - return True for file, sha1 in fileAndSha1: if not osp.exists(file): rv.append({"file": file, "result": False}) continue - if _filter(file, sha1): + if _filter is None or _filter(file, sha1): # check sha1 fileSha1 = hashlib.sha1(readBytesFile(file)).hexdigest() rv.append({"file": file, "result": fileSha1 == sha1}) @@ -325,3 +326,87 @@ def getAvailableAuthorServer() -> Optional[str]: except ConnectionError: continue return None + + +def getCurrentMainFile() -> str: + """ + 返回可执行文件 / 脚本的路径 + """ + return sys.argv[0] + + +def setStartOnStartup(): + """ + Decide which method to run according to the operating system + """ + if system() == "Windows": + setStartOnStartupWindows() + elif system() == "Linux": + setStartOnStartupLinux() + + +def setStartOnStartupLinux(): + """ + 在相应位置创建一个快捷方式, 使得本应用能够开机自启动 + 仅限于 Linux 操作系统 + """ + raise NotImplementedError("You cannot currently do this.") + + +def setStartOnStartupWindows(): + """ + 在相应位置创建一个快捷方式, 使得本应用能够开机自启动 + 仅限于 Windows 操作系统 + """ + # Refs: + # - https://github.com/pearu/iocbio/blob/master/installer/utils.py + # - https://blog.csdn.net/thundor/article/details/5968581 + + # Operating system import check + from win32comext.shell import shell + import pythoncom + + targetDirectory = os.getenv('USERPROFILE') + r'\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup' + shortcut = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, + pythoncom.CLSCTX_INPROC_SERVER, + shell.IID_IShellLink) + pythonPath = sys.executable.replace("python.exe", "pythonw.exe") # 可执行文件全路径 + if not os.path.exists(pythonPath): + pythonPath = sys.executable + if getCurrentMainFile().endswith(".exe"): + pythonPath = "" + shortcut.SetPath(pythonPath) + shortcut.SetArguments(getCurrentMainFile()) + shortcut.SetDescription(pythonPath) + shortcut.SetIconLocation(sys.executable, 0) + shortcut.QueryInterface(pythoncom.IID_IPersistFile).Save(targetDirectory + r"\MCSL2.lnk", 0) # 保存快捷方式文件 + + +def removeStartOnStartup(): + """ + 移除先前创建的开机自启动快捷方式 + """ + if system() == "Windows": + removeStartOnStartupWindows() + elif system() == "Linux": + removeStartOnStartupLinux() + + +def removeStartOnStartupLinux(): + """ + 移除先前创建的开机自启动快捷方式 + 仅限于 Linux 操作系统 + """ + raise NotImplementedError("You cannot currently do this.") + + +def removeStartOnStartupWindows(): + """ + 移除先前创建的开机自启动快捷方式 + 仅限于 Windows 操作系统 + """ + shortcut = (os.getenv('USERPROFILE') + + r"\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\MCSL2.lnk") + if not os.path.exists(shortcut): + raise FileNotFoundError(f"{shortcut} not found! Check again or ask others for help") + os.remove(shortcut) diff --git a/MCSL2Lib/variables.py b/MCSL2Lib/variables.py index c4df59d..4003e4a 100644 --- a/MCSL2Lib/variables.py +++ b/MCSL2Lib/variables.py @@ -15,8 +15,8 @@ """ from MCSL2Lib.ProgramControllers.settingsController import cfg -from MCSL2Lib.utils import readGlobalServerConfig from MCSL2Lib.singleton import Singleton +from MCSL2Lib.utils import readGlobalServerConfig class BaseServerVariables: