最近我在csdn上看见了 __小小的程序员__ 博主的文章,发现python可以开发minecraft启动器?

好牛皮!我试了一下!果真可以,所有版本(包括最新1.19和最新快照)都可以。别忘了pip install pyqt5和minecraft-launcher-lib哦

效果:

选择路径,颜色界面
选择版本和启动游戏界面
版本安装界面

 下载地址:

github链接

gitee链接

希望此文对你有帮助。 

这一个程序可以自己下载minecraft java edition(包括beta版本,远古版!),如果可以自己改动代码的话首先要下载qt designer,这是python里pyqt里面的一个图形化界面设计应用。

关键的是,下载这些东西都可以不用钱了!动了小心思。

提醒:大家不要沉迷哦

程序的一部分:

1.主程序 launcher.py

import subprocess
import sys
from threading import Thread
from time import sleep
import time
import traceback
# PyQt5中使用的基本控件都在PyQt5.QtWidgets模块中
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import *
from PyQt5 import QtCore
from json import dumps, load, dump, loads
# 导入designer工具生成的login模块
from ui import Ui_MainWindow
import minecraft_launcher_lib
import ctypes
import inspect
from rich.console import Console
from qt_material import apply_stylesheet, list_themes
console = Console()

FLAG = True
LOCK = False
ILOCK = False
verlistpub = []

try:
    # def setTip(obj):
    # myWin.status.setText(obj)

    def startfile(filename):
        try:
            os.startfile(filename)
        except:
            subprocess.Popen(['open', filename])

    def _async_raise(tid, exctype):
        """raises the exception, performs cleanup if needed"""
        try:
            tid = ctypes.c_long(tid)
            if not inspect.isclass(exctype):
                exctype = type(exctype)
            res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
                tid, ctypes.py_object(exctype))
            if res == 0:
                # pass
                raise ValueError("invalid thread id")
            elif res != 1:
                # """if it returns a number greater than one, you're in trouble,
                # and you should call it again with exc=NULL to revert the effect"""
                ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
                raise SystemError("PyThreadState_SetAsyncExc failed")
        except Exception as err:
            print(err)

    def info(*obj):
        localtime = time.localtime(time.time())
        stackinfo = traceback.extract_stack()
        who = stackinfo[1][2]
        line = stackinfo[1][1]
        # console.print("[black on green] I ", *obj)
        print(
            f"[{localtime[3]:0>2}:{localtime[4]:0>2}:{localtime[5]:0>2}] [{who:>20} |{line:>5} ] [INFO]", *obj)

    def warn(*obj):
        # console.log("[black on yellow] W ", *obj)
        localtime = time.localtime(time.time())
        stackinfo = traceback.extract_stack()
        who = stackinfo[1][2]
        line = stackinfo[1][1]
        # console.print("[black on green] I ", *obj)
        print(
            f"[{localtime[3]:0>2}:{localtime[4]:0>2}:{localtime[5]:0>2}] [{who:>20} |{line:>5} ] [WARN]", *obj)

    def error(*obj):
        # console.log("[black on red] E ", *obj)
        localtime = time.localtime(time.time())
        stackinfo = traceback.extract_stack()
        who = stackinfo[1][2]
        line = stackinfo[1][1]
        # console.print("[black on green] I ", *obj)
        print(
            f"[{localtime[3]:0>2}:{localtime[4]:0>2}:{localtime[5]:0>2}] [{who:>20} |{line:>5} ] [ERROR]", *obj)

    def readData():
        try:
            with open(".hj.json") as j:
                data = load(j)
                # data["mclist"] = minecraft_launcher_lib.utils.get_installed_versions(
                # data["mcdirs"])
                info("Read File Succ")
        except:
            warn("File Not Found ignore it")
            temp = {}
            temp["mcdirs"] = minecraft_launcher_lib.utils.get_minecraft_directory()
            temp["user"] = "Launcher"
            temp["theme"] = list_themes()[0]
            # temp["mclist"] = minecraft_launcher_lib.utils.get_installed_versions(
            # temp["mcdirs"])
            with open(".hj.json", 'w') as j:
                dump(temp, j)
            data = temp
        return data

    def reloadver():
        alllist = minecraft_launcher_lib.utils.get_version_list()
        ver = []
        for i in alllist:
            if(i["type"] == "snapshot"):
                ver.append(i["id"])
            elif(i["type"] == "release"):
                ver.append(i["id"])
            else:
                ver.append(i["id"])

        return ver

    info("Launcher is Launching")

    def stop_thread(thread):
        """终止线程"""
        _async_raise(thread.ident, SystemExit)

    class IoThread(Thread):
        def __init__(self):
            Thread.__init__(self)
            self.data = ""
            self.counter = 0

        def run(self):
            info("Start IoThread")
            while(FLAG):
                if(self.counter == 1):
                    # print("Counter!")
                    self.data = readData()
                    # self.reloadver()
                    MyMainForm.apiSetConf(self, self.data)
                if(self.counter >= 6000):
                    self.counter = 0
                    # print("s")
                self.counter += 1
                sleep(0.1)
                # print(self.counter)

    class MyMainForm(QMainWindow, Ui_MainWindow):
        def __init__(self, Parent=None):
            global myWin
            global verlistpub
            global app
            super(MyMainForm, self).__init__(Parent)
            self.setupUi(self)
            self.mclist = []
            # self.setWindowFlags(Qt.FramelessWindowHint)  # Hide Board
            self.tabWidget.currentChanged.connect(self.tabchange)
            self.launchGameBtn.clicked.connect(self.launchGameFunc)
            self.conf = readData()
            if(not os.path.exists(self.conf["mcdirs"])):
                error("Folder Is NOT FOUND")
                warn("Not Found Ignore it")
                self.conf["mcdirs"] = minecraft_launcher_lib.utils.get_minecraft_directory()
                with open('./.hj.json', 'w') as f:
                    dump(self.conf, f)

            self.conf = readData()
            self.userName.setText(self.conf["user"])
            self.userName.editingFinished.connect(self.setUserText)
            self.label_4.setWordWrap(True)
            self.stauts.setWordWrap(True)
            self.stauts.setAlignment(QtCore.Qt.AlignTop)
            self.mcDirText.setText(self.conf["mcdirs"])
            self.setMcDirButton.clicked.connect(self.setMcDir)
            self.label_4.setAlignment(QtCore.Qt.AlignTop)
            # self.closeButton.clicked.connect(self.closeFunc)
            try:
                self.choiceVer = self.mclist[0]["id"]
            except:
                pass
            self.ttemp = reloadver()
            for i in self.ttemp:
                self.selectInstallVersionBox.addItem(i)
            self.installGameButton.clicked.connect(self.installGameFunc)
            self.selectVersionBox.activated[str].connect(self.setMcVersion)
            self.styleBox.activated[str].connect(self.changeThemes)
            self.selectInstallVersionBox.activated[str].connect(
                self.setinstallMcVersion)
            self.version = "1.1.1"
            self.choiceTheme = self.conf["theme"]
            for i in list_themes():
                self.styleBox.addItem(i)
            self.threadpool = []
            try:
                self.mclist = minecraft_launcher_lib.utils.get_installed_versions(
                    self.conf["mcdirs"])
            except:
                pass
            self.lock = False
            self.desc = ""
            self.progress = 0
            # self.ttemp = reloadver()
            self.current_max = 0
            for i in self.mclist:
                self.selectVersionBox.addItem(i["id"])
            # self.setsText("asdfasdflkahsdfkjahsdf")
            self.threadpool.append(IoThread())
            for index in range(len(self.threadpool)):
                self.threadpool[index].start()
            self.installchoiceVer = self.ttemp[0]
            # .stauts.setText("sadfasdf")

        # def mousePressEvent(self, event):
        #     if event.button() == Qt.LeftButton:
        #         self.m_flag = True
        #         self.m_Position = event.globalPos()-self.pos()  # 获取鼠标相对窗口的位置
        #         event.accept()
        #         self.setCursor(QCursor(Qt.OpenHandCursor))  # 更改鼠标图标

        # def mouseMoveEvent(self, QMouseEvent):
        #     if Qt.LeftButton and self.m_flag:
        #         self.move(QMouseEvent.globalPos()-self.m_Position)  # 更改窗口位置
        #         QMouseEvent.accept()

        # def mouseReleaseEvent(self, QMouseEvent):
        #     self.m_flag = False
        #     self.setCursor(QCursor(Qt.ArrowCursor))
            try:
                apply_stylesheet(app, theme=self.conf["theme"])
                info("Succ Load Theme!")
            except:
                error(
                    f"The Launcher is Not Supported This Theme:{self.conf['theme']}")
                self.conf["theme"] = list_themes()[0]
                info(f"Change Theme:{self.conf['theme']}")
                apply_stylesheet(app, theme=self.conf["theme"])
                with open("./.hj.json", 'w') as f:
                    dump(self.conf, f)

        def setMcDir(self):
            result = QFileDialog.getExistingDirectory(
                None, "选取我的世界目录", self.conf["mcdirs"])
            info("Dir:", result)
            if(result is None or result == ""):
                warn("The Folder is Not Found")
            else:
                self.conf["mcdirs"] = result
                with open("./.hj.json", 'w') as f:
                    dump(self.conf, f)
            self.conf = readData()
            self.mcDirText.setText(self.conf["mcdirs"])

        def tabchange(self):
            index = self.tabWidget.currentIndex()
            info(f"Tab is Changed! Tabindex:{index}")
            # if self.tabWidget.currentIndex()==0
            if index == 1:
                pass
                # self.ttemp = reloadver()
                # for i in self.ttemp:
                #     self.selectInstallVersionBox.addItem(i)
            elif index == 0:
                try:
                    self.mclist = minecraft_launcher_lib.utils.get_installed_versions(
                        self.conf["mcdirs"])
                except:
                    self.mclist = []
                try:
                    self.choiceVer = self.mclist[0]["id"]
                except:
                    pass
                self.selectVersionBox.clear()
                for i in self.mclist:
                    self.selectVersionBox.addItem(i["id"])

        def installGameFunc(self):
            global ILOCK
            if(not ILOCK):
                ILOCK = True
                self.setTipText("准备安装版本:{}".format(self.installchoiceVer))
                info("Start install Version:{}".format(self.installchoiceVer))
                installmc = InstallMc(
                    self.installchoiceVer, self.conf["mcdirs"])
                installmc.start()
                self.setTipText("安装中...")

            else:
                self.setTipText("安装正在运行,请等待安装结束")
                warn("It's installing!")

        def changeThemes(self, text):
            global app
            self.conf["theme"] = text
            info(f"Change Theme:{text}")
            apply_stylesheet(app, theme=self.conf["theme"])
            with open("./.hj.json", 'w') as f:
                dump(self.conf, f)

        def setTipText(self, text):
            self.stauts.setText(text)

        def setUserText(self):
            self.conf["user"] = "".join(self.userName.text().split(" "))
            with open(".hj.json", 'w') as j:
                dump(self.conf, j)

        def setinstallMcVersion(self, text):
            self.installchoiceVer = text
            # print(text)

        def setMcVersion(self, text):
            self.choiceVer = text
            # print(text)

        def launchGameFunc(self):
            global LOCK
            if(not LOCK):
                LOCK = True
                info("Run Mc")
                self.setTipText("游戏启动中...")
                mc = RunMc(
                    self.choiceVer, self.conf["mcdirs"], self.userName.text(), "8888-8888-8888")
                mc.start()
                self.setTipText("游戏已启动等待游戏窗口出现...")
                info(self.userName.text())

            else:
                self.setTipText("游戏正在运行,请等待游戏结束")
                warn("The Game is running but User want to Creat NEW game thread!")
                warn("New Thread is kill by USER")

        def closeEvent(self, event):
            global FLAG
            FLAG = False
            for index in range(len(self.threadpool)):
                stop_thread(self.threadpool[index])

        def apiSetConf(self, conf: dict):
            self.config = conf

    class InstallMc(Thread):
        def __init__(self, version, mcdir):
            Thread.__init__(self)
            self.version = version
            self.mcdir = mcdir
            self.desc = ""
            self.current_max = 0
            self.progress = 0

        def set_max(self, new_max: int):
            self.current_max = new_max
            info(f"{self.desc} Finish")
            info("Start New Task!")

        def set_status(self, status: str):
            self.desc = status
            info(f"Change Task To:{status}")

        def set_progress(self, progress: int):
            global myWin
            if self.current_max != 0:
                self.progress = progress
                myWin.stauts.setText(
                    f"下载中: 已下载:{progress} 未下载:{self.current_max-progress}")
                info(
                    f"{self.desc}: Progress:{progress} Total:{self.current_max}")
                # MyMainForm.setTipText(MyMainForm, "s")

        def run(self):
            global ILOCK, myWin
            callback = {
                "setStatus": self.set_status,
                "setProgress": self.set_progress,
                "setMax": self.set_max
            }
            try:
                minecraft_launcher_lib.install.install_minecraft_version(
                    self.version, self.mcdir, callback)
                info(f"Succ Install Version:{self.version}")
                ILOCK = False
                myWin.stauts.setText(f"成功安装版本:{self.version}")
            except:
                myWin.stauts.setText("安装异常,未知版本!")

    class RunMc(Thread):
        def __init__(self, version, mcdir, username, uuuid):
            Thread.__init__(self)
            self.version = version
            self.mcdir = mcdir
            self.username = username
            self.uuid = uuuid
            self.desc = ""
            self.current_max = 0
            self.progress = 0

        def set_max(self, new_max: int):
            self.current_max = new_max
            info("Download Finish")
            info("Start New Task!")

        def set_status(self, status: str):
            self.desc = status
            info(f"Change Task To:{status}")

        def set_progress(self, progress: int):
            if self.current_max != 0:
                self.progress = progress
                myWin.stauts.setText(
                    f"启动中: 已准备:{progress} 未准备:{self.current_max-progress}")
                info(
                    f"{self.desc}: Progress:{progress} Total:{self.current_max}")

        def run(self):
            global LOCK
            callback = {
                "setStatus": self.set_status,
                "setProgress": self.set_progress,
                "setMax": self.set_max
            }
            try:
                minecraft_launcher_lib.install.install_minecraft_version(
                    self.version, self.mcdir, callback)
                options = {
                    "username": self.username,
                    "uuid": self.uuid,
                    "token": self.uuid,
                    "-Dminecraft.launcher.brand": "Huaji Launcher",
                    "-Dminecraft.launcher.version": "1.1.1"
                    # "launcherName": "PMCL",  # The name of your launcher
                    # "launcherVersion": "1.1",  # The version of your launcher
                }
                minecraft_command = minecraft_launcher_lib.command.get_minecraft_command(
                    self.version, self.mcdir, options)
                myWin.stauts.setText("成功生成启动命令")
                # 最终,你可以直接启动Minecraft了
                subprocess.call(minecraft_command)
                info("Run Minecraft Command with:{}".format(minecraft_command))
                info("Mc is Run!")
                info("----------Log With Mc-----------")
                myWin.stauts.setText("等待游戏窗口出现...")
                subprocess.call(
                    minecraft_command)
                LOCK = False
                myWin.stauts.setText("游戏已退出")
            except:
                myWin.stauts.setText("安装异常,未知版本!")

    if __name__ == "__main__":

        # 固定的,PyQt5程序都需要QApplication对象。sys.argv是命令行参数列表,确保程序可以双击运行
        app = QApplication(sys.argv)
        # 初始化
        myWin = MyMainForm()
        # 将窗口控件显示在屏幕上
        myWin.show()
        # 程序运行,sys.exit方法确保程序完整退出。
        sys.exit(app.exec_())
except SystemExit:
    pass
except:
    error("Launcher Error:")
    error("Looking For Trace Back:")
    console.print_exception(show_locals=True)

2.ui界面设计 main.py

# Form implementation generated from reading ui file '/Users/dcboy/Desktop/pml/main.ui'
#
# Created by: PyQt6 UI code generator 6.1.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(389, 345)
        MainWindow.setMinimumSize(QtCore.QSize(389, 345))
        MainWindow.setMaximumSize(QtCore.QSize(389, 345))
        MainWindow.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
        self.tabWidget.setGeometry(QtCore.QRect(10, 8, 371, 301))
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.groupBox = QtWidgets.QGroupBox(self.tab)
        self.groupBox.setGeometry(QtCore.QRect(10, 10, 341, 211))
        self.groupBox.setObjectName("groupBox")
        self.selectVersionBox = QtWidgets.QComboBox(self.groupBox)
        self.selectVersionBox.setGeometry(QtCore.QRect(80, 31, 191, 41))
        self.selectVersionBox.setObjectName("selectVersionBox")
        self.label = QtWidgets.QLabel(self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 40, 60, 21))
        self.label.setObjectName("label")
        self.launchGameBtn = QtWidgets.QPushButton(self.groupBox)
        self.launchGameBtn.setGeometry(QtCore.QRect(190, 150, 141, 51))
        self.launchGameBtn.setObjectName("launchGameBtn")
        self.tabWidget.addTab(self.tab, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.groupBox_2 = QtWidgets.QGroupBox(self.tab_2)
        self.groupBox_2.setGeometry(QtCore.QRect(10, 10, 341, 131))
        self.groupBox_2.setObjectName("groupBox_2")
        self.selectInstallVersionBox = QtWidgets.QComboBox(self.groupBox_2)
        self.selectInstallVersionBox.setGeometry(QtCore.QRect(10, 40, 321, 41))
        self.selectInstallVersionBox.setObjectName("selectInstallVersionBox")
        self.installGameButton = QtWidgets.QPushButton(self.groupBox_2)
        self.installGameButton.setGeometry(QtCore.QRect(12, 90, 131, 32))
        self.installGameButton.setObjectName("installGameButton")
        self.tabWidget.addTab(self.tab_2, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.groupBox_3 = QtWidgets.QGroupBox(self.tab_3)
        self.groupBox_3.setGeometry(QtCore.QRect(10, 10, 341, 71))
        self.groupBox_3.setObjectName("groupBox_3")
        self.userName = QtWidgets.QLineEdit(self.groupBox_3)
        self.userName.setGeometry(QtCore.QRect(90, 30, 241, 31))
        self.userName.setObjectName("userName")
        self.label_2 = QtWidgets.QLabel(self.groupBox_3)
        self.label_2.setGeometry(QtCore.QRect(20, 30, 61, 31))
        self.label_2.setObjectName("label_2")
        self.groupBox_4 = QtWidgets.QGroupBox(self.tab_3)
        self.groupBox_4.setGeometry(QtCore.QRect(10, 90, 341, 91))
        self.groupBox_4.setObjectName("groupBox_4")
        self.label_3 = QtWidgets.QLabel(self.groupBox_4)
        self.label_3.setGeometry(QtCore.QRect(10, 30, 60, 16))
        self.label_3.setObjectName("label_3")
        self.mcDirText = QtWidgets.QLabel(self.groupBox_4)
        self.mcDirText.setGeometry(QtCore.QRect(50, 30, 281, 16))
        self.mcDirText.setObjectName("mcDirText")
        self.setMcDirButton = QtWidgets.QPushButton(self.groupBox_4)
        self.setMcDirButton.setGeometry(QtCore.QRect(12, 50, 321, 32))
        self.setMcDirButton.setObjectName("setMcDirButton")
        self.groupBox_5 = QtWidgets.QGroupBox(self.tab_3)
        self.groupBox_5.setGeometry(QtCore.QRect(10, 190, 201, 71))
        self.groupBox_5.setObjectName("groupBox_5")
        self.styleBox = QtWidgets.QComboBox(self.groupBox_5)
        self.styleBox.setGeometry(QtCore.QRect(10, 30, 181, 32))
        self.styleBox.setObjectName("styleBox")
        self.tabWidget.addTab(self.tab_3, "")
        self.stauts = QtWidgets.QLabel(self.centralwidget)
        self.stauts.setGeometry(QtCore.QRect(60, 310, 321, 31))
        self.stauts.setObjectName("stauts")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(10, 310, 41, 31))
        self.label_4.setObjectName("label_4")
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Launcher Main"))
        self.groupBox.setTitle(_translate("MainWindow", "启动"))
        self.label.setText(_translate("MainWindow", "选择版本"))
        self.launchGameBtn.setText(_translate("MainWindow", "启动游戏"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "主页面"))
        self.groupBox_2.setTitle(_translate("MainWindow", "安装版本"))
        self.installGameButton.setText(_translate("MainWindow", "安装"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "下载"))
        self.groupBox_3.setTitle(_translate("MainWindow", "用户"))
        self.label_2.setText(_translate("MainWindow", "用户名"))
        self.groupBox_4.setTitle(_translate("MainWindow", "MC文件夹"))
        self.label_3.setText(_translate("MainWindow", "当前"))
        self.mcDirText.setText(_translate("MainWindow", "NONE"))
        self.setMcDirButton.setText(_translate("MainWindow", "选择..."))
        self.groupBox_5.setTitle(_translate("MainWindow", "主题样式"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "设置"))
        self.stauts.setText(_translate("MainWindow", "启动启动器"))
        self.label_4.setText(_translate("MainWindow", "状态:"))

功能:

改变界面颜色,文件路径,下载版本,启动游戏


截图:(没有骗你,是java版本,下载下来的,不是launcher或者其他地方的图片)

 是不是很nice?这就是java版本。和原版一点也没有区别!

希望点个赞,关注一下我和 __小小的程序员__哦!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐