diff --git a/forge_download.py b/forge_download.py new file mode 100644 index 0000000..5d0964b --- /dev/null +++ b/forge_download.py @@ -0,0 +1,25 @@ +from wget import download +from zipfile import ZipFile +from json import loads +import minecraft_launcher_lib as mll +import threading + + +def download_forge_installer(version): + global installer + installer = f"forge-{version}-installer.jar" + url = f"https://files.minecraftforge.net/maven/net/minecraftforge/forge/{version}/forge-{version}-installer.jar" + download(url) + + + +def download_forge(version): + download_forge_installer(version) + zf = ZipFile(installer, "r") + with zf.open("install_profile.json", "r") as f: + version_content = f.read() + version_data = loads(version_content) + forge_ver = version_data["version"] + minecraft_ver = version_data["minecraft"] + + diff --git a/funcs.py b/funcs.py index be3f03f..3705bb0 100644 --- a/funcs.py +++ b/funcs.py @@ -1,3 +1,5 @@ +import os + from PyQt5.QtWidgets import QPushButton, QLabel, QDesktopWidget from PyQt5.QtCore import Qt import web_request @@ -9,7 +11,7 @@ from shutil import move, rmtree from pathlib import Path from random import choice -from threads import download_thread +from threads import download_thread, run_thread from requests import get @@ -26,9 +28,12 @@ def center(cls): def mp_names_loop(cls, mp_names, clicked_connect=none_test): pos = 130 - width = len(max(mp_names, key=len)) * 8 + names = [] + for i in mp_names: + names.append(i["mp_name"]) + width = len(max(names, key=len)) * 8 for i in mp_names: - cls.mp_label = QLabel(i, cls) + cls.mp_label = QLabel(i["mp_name"], cls) cls.mp_label.move(0, pos) cls.mp_label.resize(width, cls.mp_label.height()) cls.mp_label.setAlignment(Qt.AlignLeft) @@ -39,7 +44,7 @@ def mp_names_loop(cls, mp_names, clicked_connect=none_test): cls.mp_button.move(width, pos) cls.mp_button.setStyleSheet("background:rgba(185,187,190,255)") cls.mp_button.clicked.connect(clicked_connect) - cls.mp_button.setObjectName(i) + cls.mp_button.setObjectName(i["mp_name"]) pos += 30 link.init(cls, "https://mpdb.xyz", pos + 30) @@ -58,8 +63,8 @@ def change_button_text(cls, value): cls.run_button.setText(f"Run {value}") cls.selected_mp = value cls.forge_selector.clear() - info = loads(get("https://mpdb.xyz/get_data.php?data=true&from=name&name=" + value).text) - cls.forge_selector.addItems(get_forges(info["version"])) + info = loads(get(f"https://mpdb.xyz/api/modpack.php?name={value}").text) + cls.forge_selector.addItems(get_forges(info["mp_version"])) cls.run_button.resize(len("Run " + value) * 8, cls.run_button.height()) cls.username_selector.move(cls.width() - cls.run_button.width() - cls.username_selector.width(), 0) cls.run_button.move(cls.width() - cls.run_button.width(), 0) @@ -69,7 +74,8 @@ def change_button_text(cls, value): def download_modpack(name, author, data): global display_name, download_url print(f"Downloading {name} by {author}!") - download(f"https://mpdb.xyz/modpacks/{author}_{name}.zip") + download(f"http://185.255.94.159/modpacks/{author}/{name}.zip") + os.rename(f"{name}.zip", f"{author}_{name}.zip") zip_path = f"{author}_{name}.zip" if name not in data["downloaded"]: data["downloaded"].append(name) @@ -125,7 +131,7 @@ def start_download_worker(cls, mp_name, data): cls.run_minecraft_info.setText(f"Downloading {mp_name}") cls.run_minecraft_info.resize(len(f"Downloading {mp_name}") * 8, cls.run_minecraft_info.height()) for i in cls.mp_names: - b = cls.findChild(QPushButton, i) + b = cls.findChild(QPushButton, i["mp_name"]) b.setEnabled(False) cls.run_button.setEnabled(False) cls.modpack_selector.setEnabled(False) @@ -137,8 +143,9 @@ def start_download_worker(cls, mp_name, data): def stop_download_worker(): thread.stop() cl.run_minecraft_info.setText("") + print(cl.mp_names) for i in cl.mp_names: - b = cl.findChild(QPushButton, i) + b = cl.findChild(QPushButton, i["mp_name"]) b.setEnabled(True) b = cl.findChild(QPushButton, modpack_name) b.hide() @@ -156,3 +163,34 @@ def stop_download_worker(): def get_forges(version=None): if version is not None: return forges[version] + + +def start_run_worker(mc_version, forge_version, mp_name, username, ram, cls): + global run + if username == "": + run = run_thread.RunThread(mc_version=mc_version, forge_version=forge_version, mp_name=mp_name, ram=ram) + else: + run = run_thread.RunThread(mc_version=mc_version, forge_version=forge_version, mp_name=mp_name, + username=username, ram=ram) + run.start() + run.any_signal.connect(lambda cls=cls: stop_run_worker(cls)) + +def stop_run_worker(cls): + cls.run_button.setEnabled(True) + run.stop() + + +def download_file(url, path=""): + with open(path, "wb") as f: + r = get(url, stream=True) + total_length = r.headers.get("content-length") + if total_length is None: + print(r.content) + else: + total_length = int(total_length) + for data in r.iter_content(chunk_size=4096): + f.write(data) + + + + diff --git a/mail.py b/mail.py new file mode 100644 index 0000000..33fe9b7 --- /dev/null +++ b/mail.py @@ -0,0 +1,6 @@ +from requests import get + + + +def send_email(e): + get("https://mpdb.xyz/mail.php?e="+e) \ No newline at end of file diff --git a/mpdb.py b/mpdb.py index d939401..b0fa280 100644 --- a/mpdb.py +++ b/mpdb.py @@ -4,6 +4,7 @@ from PyQt5.QtWidgets import QApplication, QMainWindow from PyQt5.QtGui import QCursor +from PyQt5 import QtCore from PyQt5.QtCore import Qt from time import perf_counter import data @@ -11,8 +12,11 @@ from get_ram import get_ram import funcs from ui import modpack_selector, ram_selector, username_selector, discord_image, run_button, run_minecraft_info, \ - forge_selector + forge_selector, progress_bar from webbrowser import open as wb_open +from mail import send_email +from requests import get +from json import loads run_timer = perf_counter() @@ -26,11 +30,13 @@ class MPDB(QMainWindow): try: def __init__(self): super().__init__() + # self.resize(1000, 600) + # progress_bar.init(self) def initUI(self): - self.mp_names = web_request.get_data("https://mpdb.xyz/get_data.php") + self.mp_names = web_request.get_data("https://mpdb.xyz/api/modpack.php") self.rams = [] self.data = {} self.selected_mp = "" @@ -40,14 +46,14 @@ def initUI(self): else: self.data = data.get_data("data.txt") if len(self.data["downloaded"]) != 0: - # TODO: Buraya run butonunu disable etmeyi ekle pass for ram in range(get_ram() - 1, 0, -1): self.rams.append(str(ram) + " GB") - self.resize(1000, 600) + self.setWindowTitle("TheModpackDatabase") + self.resize(1000, 600) self.setStyleSheet("background:rgba(54,57,63,255)") self.setMinimumSize(839, 438) self.setMouseTracking(True) @@ -56,10 +62,13 @@ def initUI(self): ram_selector.init(self, self.rams) username_selector.init(self) discord_image.init(self, "https://mpdb.xyz/discord.png") - run_button.init(self, f"RUN {modpack_selector.currentText(self)}") + run_button.init(self, f"RUN {modpack_selector.currentText(self)}", clicked_connect=self.start_runner) self.run_button.resize(len(self.run_button.text()) * 8, self.run_button.height()) run_minecraft_info.init(self) forge_selector.init(self, 0) + # progress_bar.init(self) + # print(self.progress_bar) + if len(self.data["downloaded"]) != 0: self.run_button.setEnabled(True) @@ -85,13 +94,33 @@ def mouseMoveEvent(self, event): def mousePressEvent(self, event): if event.buttons() == Qt.LeftButton: if event.x() <= 64 and event.y() <= 64: - wb_open("https://discord.gg/frWGTqhFbn", new=0, autoraise=True) + wb_open("https://discord.gg/frWGTqhFbn") def resizeEvent(self, event): self.run_button.move(self.width() - self.run_button.width(), 0) self.run_minecraft_info.move(self.width() - self.run_minecraft_info.width(), 30) self.username_selector.move(self.width() - self.run_button.width() - self.username_selector.width(), 0) + def start_runner(self): + self.run_button.setEnabled(False) + info = loads(get("https://mpdb.xyz/api/modpack.php?name=" + self.modpack_selector.currentText()).text) + funcs.start_run_worker(info["mp_version"], info["mp_forge"], info["mp_name"], self.username_selector.text(), + self.ram_selector.currentText(), self) + + def start_run_worker(self, mc_version, forge_version, mp_name): + self.run = RunThread(mc_version=mc_version, forge_version=forge_version, mp_name=mp_name) + self.run.start() + self.run.any_signal.connect(self.stop_run_worker) + + + def stop_run_worker(self): + self.run_button.setEnabled(True) + self.run.stop() + + def changeValue(self, value): + progress_bar.init(self) + self.progress_bar.setValue(value) + @@ -100,6 +129,44 @@ def resizeEvent(self, event): print(e) +class RunThread(QtCore.QThread): + any_signal = QtCore.pyqtSignal() + + def __init__(self, parent=None, mc_version="", forge_version="", mp_name=""): + super(RunThread, self).__init__(parent) + self.mc_version = mc_version + self.forge_version = forge_version + self.mp_name = mp_name + + + def run(self): + mpdb = MPDB() + print(f"Running {self.forge_version} for {self.mc_version}") + link = f"https://mpdb.xyz/forge/{self.mc_version.replace('.','-')}/{self.forge_version}.zip" + file_name = f"{self.forge_version}.zip" + with open(file_name, "wb") as f: + print(f"Downloading {file_name}") + r = get(link, stream=True) + total_length = r.headers.get("content-length") + if total_length is None: + print(r.content) + else: + dl = 0 + total_length = int(total_length) + for data in r.iter_content(chunk_size=4096): + dl += len(data) + f.write(data) + done = int(100 * dl / total_length) + mpdb.changeValue(done) + print("\r" + str(done)) + + self.any_signal.emit() + + def stop(self): + print("Stopping") + self.wait() + + if __name__ == "__main__": app = QApplication(argv) mpdb = MPDB() diff --git a/rpc.py b/rpc.py new file mode 100644 index 0000000..77a4c32 --- /dev/null +++ b/rpc.py @@ -0,0 +1,197 @@ +from abc import ABCMeta, abstractmethod +import json +import logging +import os +import socket +import sys +import struct +import uuid + +OP_HANDSHAKE = 0 +OP_FRAME = 1 +OP_CLOSE = 2 +OP_PING = 3 +OP_PONG = 4 + +logger = logging.getLogger(__name__) + + +class DiscordIpcError(Exception): + pass + + +class DiscordIpcClient(metaclass=ABCMeta): + + """Work with an open Discord instance via its JSON IPC for its rich presence API. + In a blocking way. + Classmethod `for_platform` + will resolve to one of WinDiscordIpcClient or UnixDiscordIpcClient, + depending on the current platform. + Supports context handler protocol. + """ + + def __init__(self, client_id): + self.client_id = client_id + self._connect() + self._do_handshake() + logger.info("connected via ID %s", client_id) + + @classmethod + def for_platform(cls, client_id, platform=sys.platform): + if platform == 'win32': + return WinDiscordIpcClient(client_id) + else: + return UnixDiscordIpcClient(client_id) + + @abstractmethod + def _connect(self): + pass + + def _do_handshake(self): + ret_op, ret_data = self.send_recv({'v': 1, 'client_id': self.client_id}, op=OP_HANDSHAKE) + # {'cmd': 'DISPATCH', 'data': {'v': 1, 'config': {...}}, 'evt': 'READY', 'nonce': None} + if ret_op == OP_FRAME and ret_data['cmd'] == 'DISPATCH' and ret_data['evt'] == 'READY': + return + else: + if ret_op == OP_CLOSE: + self.close() + raise RuntimeError(ret_data) + + @abstractmethod + def _write(self, date: bytes): + pass + + @abstractmethod + def _recv(self, size: int) -> bytes: + pass + + def _recv_header(self) -> (int, int): + header = self._recv_exactly(8) + return struct.unpack(" bytes: + buf = b"" + size_remaining = size + while size_remaining: + chunk = self._recv(size_remaining) + buf += chunk + size_remaining -= len(chunk) + return buf + + def close(self): + logger.warning("closing connection") + try: + self.send({}, op=OP_CLOSE) + finally: + self._close() + + @abstractmethod + def _close(self): + pass + + def __enter__(self): + return self + + def __exit__(self, *_): + self.close() + + def send_recv(self, data, op=OP_FRAME): + self.send(data, op) + return self.recv() + + def send(self, data, op=OP_FRAME): + logger.debug("sending %s", data) + data_str = json.dumps(data, separators=(',', ':')) + data_bytes = data_str.encode('utf-8') + header = struct.pack(" (int, "JSON"): + """Receives a packet from discord. + Returns op code and payload. + """ + op, length = self._recv_header() + payload = self._recv_exactly(length) + data = json.loads(payload.decode('utf-8')) + logger.debug("received %s", data) + return op, data + + def set_activity(self, act): + # act + data = { + 'cmd': 'SET_ACTIVITY', + 'args': {'pid': os.getpid(), + 'activity': act}, + 'nonce': str(uuid.uuid4()) + } + self.send(data) + + +class WinDiscordIpcClient(DiscordIpcClient): + + _pipe_pattern = R'\\?\pipe\discord-ipc-{}' + + def _connect(self): + for i in range(10): + path = self._pipe_pattern.format(i) + try: + self._f = open(path, "w+b") + except OSError as e: + logger.error("failed to open {!r}: {}".format(path, e)) + else: + break + else: + return DiscordIpcError("Failed to connect to Discord pipe") + + self.path = path + + def _write(self, data: bytes): + self._f.write(data) + self._f.flush() + + def _recv(self, size: int) -> bytes: + return self._f.read(size) + + def _close(self): + self._f.close() + + +class UnixDiscordIpcClient(DiscordIpcClient): + + def _connect(self): + self._sock = socket.socket(socket.AF_UNIX) + pipe_pattern = self._get_pipe_pattern() + + for i in range(10): + path = pipe_pattern.format(i) + if not os.path.exists(path): + continue + try: + self._sock.connect(path) + except OSError as e: + logger.error("failed to open {!r}: {}".format(path, e)) + else: + break + else: + return DiscordIpcError("Failed to connect to Discord pipe") + + @staticmethod + def _get_pipe_pattern(): + env_keys = ('XDG_RUNTIME_DIR', 'TMPDIR', 'TMP', 'TEMP') + for env_key in env_keys: + dir_path = os.environ.get(env_key) + if dir_path: + break + else: + dir_path = '/tmp' + return os.path.join(dir_path, 'discord-ipc-{}') + + def _write(self, data: bytes): + self._sock.sendall(data) + + def _recv(self, size: int) -> bytes: + return self._sock.recv(size) + + def _close(self): + self._sock.close() \ No newline at end of file diff --git a/threads/discord_rpc_thread.py b/threads/discord_rpc_thread.py index e5f98b1..d4b3bd2 100644 --- a/threads/discord_rpc_thread.py +++ b/threads/discord_rpc_thread.py @@ -1,28 +1,33 @@ from PyQt5 import QtCore -import discord_rpc import time +import rpc class DiscordRPCThread(QtCore.QThread): + any_signal = QtCore.pyqtSignal() + def __init__(self): - super().__init__() - self.any_signal = QtCore.pyqtSignal() + super(DiscordRPCThread, self).__init__(None) + def run(self): - discord_rpc.initialize("4b0085422009992f152be2fdf328aac0fbde4dcc8e410f26fe44b4e97d974004") - i = 0 - start = time.time() - while i < 10000000: - i += 1 - discord_rpc.update_presence( - **{ - 'details': 'Iteration # {}'.format(i), - 'start_timestamp': start, - 'large_image_key': 'sa' + client_id = "4b0085422009992f152be2fdf328aac0fbde4dcc8e410f26fe44b4e97d974004" + rpc_obj = rpc.DiscordIpcClient.for_platform(client_id) + print("RPC Connection Başarılı") + time.sleep(5) + start_time = time.mktime(time.localtime()) + while True: + activity = { + "state": "MPDB", + "details": "ModpackDatabase", + "timestamps": {"start": start_time}, + "assets": { + "small_text": "SA", + "small_image": "sa" } - ) - discord_rpc.update_connection() - time.sleep(2) + } + rpc_obj.set_activity(activity) + time.sleep(900) self.any_signal.emit() def stop(self): diff --git a/threads/download_thread.py b/threads/download_thread.py index 6d4e7d1..89d3226 100644 --- a/threads/download_thread.py +++ b/threads/download_thread.py @@ -19,10 +19,10 @@ def __init__(self, mp_name="", data="", parent=None): def run(self): print("Downloading ModPack") - r = web_request.get_data(f"https://mpdb.xyz/get_data.php?data=true&from=name&name={self.mp_name}") - self.name = r["name"] - self.author = r["author"] - self.version = r["version"] + r = web_request.get_data(f"https://mpdb.xyz/api/modpack.php?name={self.mp_name}") + self.name = r["mp_name"] + self.author = r["mp_author"] + self.version = r["mp_version"] Path(self.name).mkdir(parents=True, exist_ok=True) funcs.download_modpack(self.name, self.author, self.data) self.any_signal.emit() diff --git a/threads/forge_thread.py b/threads/forge_thread.py new file mode 100644 index 0000000..a1fb9fd --- /dev/null +++ b/threads/forge_thread.py @@ -0,0 +1,77 @@ +from PyQt5 import QtCore +from minecraft_launcher_lib.helper import * +from minecraft_launcher_lib.natives import * + + +class ForgeThread(QtCore.QThread): + any_signal = QtCore.pyqtSignal() + + def __init__(self, libs, path, callback): + super().__init__() + self.libs = libs + self.path = path + self.callback = callback + self.callback.get("setMax", empty)(len(self.libs["libraries"])) + + def run(self) -> None: + print("Yeni libs") + for count, i in enumerate(self.libs["libraries"]): + # Check, if the rules allow this lib for the current system + if not parse_rule_list(i, "rules", {}): + continue + # Turn the name into a path + currentPath = os.path.join(self.path, "libraries") + if "url" in i: + if i["url"].endswith("/"): + downloadUrl = i["url"][:-1] + else: + downloadUrl = i["url"] + else: + downloadUrl = "https://libraries.minecraft.net" + try: + libPath, name, version = i["name"].split(":") + except: + continue + for libPart in libPath.split("."): + currentPath = os.path.join(currentPath, libPart) + downloadUrl = downloadUrl + "/" + libPart + try: + version, fileend = version.split("@") + except: + fileend = "jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + name + "/" + version + currentPath = os.path.join(currentPath, name, version) + native = get_natives(i) + # Check if there is a native file + if native != "": + jarFilenameNative = name + "-" + version + "-" + native + ".jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + jarFilename + # Try to download the lib + try: + download_file(downloadUrl, os.path.join(currentPath, jarFilename), self.callback) + except: + pass + if "downloads" not in i: + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(self.path, "versions", self.data["id"], "natives"), i["extract"]) + continue + if "artifact" in i["downloads"]: + download_file(i["downloads"]["artifact"]["url"], os.path.join(currentPath, jarFilename), self.callback, + sha1=i["downloads"]["artifact"]["sha1"]) + if native != "": + download_file(i["downloads"]["classifiers"][native]["url"], + os.path.join(currentPath, jarFilenameNative), + self.callback, sha1=i["downloads"]["classifiers"][native]["sha1"]) + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(self.path, "versions", self.libs["id"], "natives"), i["extract"]) + self.callback.get("setProgress", empty)(count) + print("yeni lib1") + print("yeni lib 1 bitti") + self.any_signal.emit() + + def stop(self): + self.wait() diff --git a/threads/run_thread.py b/threads/run_thread.py index eac4a1a..0ec22f7 100644 --- a/threads/run_thread.py +++ b/threads/run_thread.py @@ -1,24 +1,367 @@ +import subprocess +import time +import tempfile +import random +import threading + +import minecraft_launcher_lib.forge from PyQt5 import QtCore -from minecraft_launcher_lib import forge, command -from subprocess import call +from sys import stdout + +from minecraft_launcher_lib.natives import extract_natives_file, get_natives +from minecraft_launcher_lib.install import install_libraries +from requests import get +from minecraft_launcher_lib import forge, install +from minecraft_launcher_lib.forge import extract_file +from minecraft_launcher_lib.helper import * +from minecraft_launcher_lib.exceptions import * +from minecraft_launcher_lib.runtime import * +from minecraft_launcher_lib.command import * +from .forge_thread import ForgeThread +import copy + + +def dict_chunkify(input_dict, max_limit): + chunks = [] + curr_dict = {} + for k, v in input_dict.items(): + if len(curr_dict.keys()) < max_limit: + curr_dict.update({k: v}) + else: + chunks.append(copy.deepcopy(curr_dict)) + curr_dict = {k: v} + # update last curr_dict + chunks.append(curr_dict) + return chunks + +def chunkify(lst, n): + return [lst[i::n] for i in range(n)] + + +def install_forge_libs(data, path, callback): + print("Forge Libs Başladı") + callback.get("setMax", empty)(len(data["libraries"])) + print("DATA: ") + lib_data1 = chunkify(data["libraries"], 2)[0] + lib_data2 = chunkify(data["libraries"], 2)[1] + def lib1(): + print("lib1 start") + for count, i in enumerate(lib_data1): + # Check, if the rules allow this lib for the current system + if not parse_rule_list(i, "rules", {}): + continue + # Turn the name into a path + currentPath = os.path.join(path, "libraries") + if "url" in i: + if i["url"].endswith("/"): + downloadUrl = i["url"][:-1] + else: + downloadUrl = i["url"] + else: + downloadUrl = "https://libraries.minecraft.net" + try: + libPath, name, version = i["name"].split(":") + except: + continue + for libPart in libPath.split("."): + currentPath = os.path.join(currentPath, libPart) + downloadUrl = downloadUrl + "/" + libPart + try: + version, fileend = version.split("@") + except: + fileend = "jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + name + "/" + version + currentPath = os.path.join(currentPath, name, version) + native = get_natives(i) + # Check if there is a native file + if native != "": + jarFilenameNative = name + "-" + version + "-" + native + ".jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + jarFilename + # Try to download the lib + try: + download_file(downloadUrl, os.path.join(currentPath, jarFilename), callback) + except: + pass + if "downloads" not in i: + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(path, "versions", data["id"], "natives"), i["extract"]) + continue + if "artifact" in i["downloads"]: + download_file(i["downloads"]["artifact"]["url"], os.path.join(currentPath, jarFilename), callback, + sha1=i["downloads"]["artifact"]["sha1"]) + if native != "": + download_file(i["downloads"]["classifiers"][native]["url"], + os.path.join(currentPath, jarFilenameNative), + callback, sha1=i["downloads"]["classifiers"][native]["sha1"]) + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(path, "versions", data["id"], "natives"), i["extract"]) + callback.get("setProgress", empty)(count) + print("lib1") + print("lib 1 bitti") + + def lib2(): + print("lib2 start") + for count, i in enumerate(lib_data2): + # Check, if the rules allow this lib for the current system + if not parse_rule_list(i, "rules", {}): + continue + # Turn the name into a path + currentPath = os.path.join(path, "libraries") + if "url" in i: + if i["url"].endswith("/"): + downloadUrl = i["url"][:-1] + else: + downloadUrl = i["url"] + else: + downloadUrl = "https://libraries.minecraft.net" + try: + libPath, name, version = i["name"].split(":") + except: + continue + for libPart in libPath.split("."): + currentPath = os.path.join(currentPath, libPart) + downloadUrl = downloadUrl + "/" + libPart + try: + version, fileend = version.split("@") + except: + fileend = "jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + name + "/" + version + currentPath = os.path.join(currentPath, name, version) + native = get_natives(i) + # Check if there is a native file + if native != "": + jarFilenameNative = name + "-" + version + "-" + native + ".jar" + jarFilename = name + "-" + version + "." + fileend + downloadUrl = downloadUrl + "/" + jarFilename + # Try to download the lib + try: + download_file(downloadUrl, os.path.join(currentPath, jarFilename), callback) + except: + pass + if "downloads" not in i: + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(path, "versions", data["id"], "natives"), i["extract"]) + continue + if "artifact" in i["downloads"]: + download_file(i["downloads"]["artifact"]["url"], os.path.join(currentPath, jarFilename), callback, + sha1=i["downloads"]["artifact"]["sha1"]) + if native != "": + download_file(i["downloads"]["classifiers"][native]["url"], + os.path.join(currentPath, jarFilenameNative), + callback, sha1=i["downloads"]["classifiers"][native]["sha1"]) + if "extract" in i: + extract_natives_file(os.path.join(currentPath, jarFilenameNative), + os.path.join(path, "versions", data["id"], "natives"), i["extract"]) + callback.get("setProgress", empty)(count) + print("lib2") + print("lib 2 bitti") + lib1t = threading.Thread(target=lib1) + lib2t = threading.Thread(target=lib2) + lib1t.start() + lib2t.start() + lib1t.join() + lib2t.join() + print("Forge Libs Bitti") + +def install_assets(data, path, callback, index, parse_count): + print("ASSET INDEX " + str(index)) + if "assetIndex" not in data: + return + # Download all assets + download_file(data["assetIndex"]["url"], os.path.join(path, "assets", "indexes", data["assets"] + ".json"), callback, sha1=data["assetIndex"]["sha1"]) + with open(os.path.join(path, "assets", "indexes", data["assets"] + ".json")) as f: + assets_data = json.load(f) + assets_data["objects"] = dict_chunkify(assets_data["objects"], len(assets_data["objects"])/parse_count)[index] + callback.get("setMax", empty)(len(assets_data["objects"])) + count = 0 + for key, value in assets_data["objects"].items(): + download_file("https://resources.download.minecraft.net/" + value["hash"][:2] + "/" + value["hash"], os.path.join(path, "assets", "objects", value["hash"][:2], value["hash"]), callback, sha1=value["hash"]) + count += 1 + print(index) + callback.get("setProgress", empty)(count) + +def do_version_install(versionid, path, callback, url=None): + if url: + download_file(url, os.path.join(path, "versions", versionid, versionid + ".json"), callback) + with open(os.path.join(path, "versions", versionid, versionid + ".json")) as f: + versiondata = json.load(f) + # For Forge + if "inheritsFrom" in versiondata: + versiondata = inherit_json(versiondata, path) + print("MC libs") + install_libraries(versiondata, path, callback) + print("Version Data: ") + print(versiondata) + print("MC libs bitti") + print("Assets Başladı") + t1 = threading.Thread(target=install_assets, args=(versiondata, path, callback, 0, 5)) + t2 = threading.Thread(target=install_assets, args=(versiondata, path, callback, 1, 5)) + t3 = threading.Thread(target=install_assets, args=(versiondata, path, callback, 2, 5)) + t4 = threading.Thread(target=install_assets, args=(versiondata, path, callback, 3, 5)) + t5 = threading.Thread(target=install_assets, args=(versiondata, path, callback, 4, 5)) + t1.start() + t2.start() + t3.start() + t4.start() + t5.start() + t1.join() + t2.join() + t3.join() + t4.join() + t5.join() + print("Assets Bitti") + # Download logging config + if "logging" in versiondata: + if len(versiondata["logging"]) != 0: + logger_file = os.path.join(path, "assets", "log_configs", versiondata["logging"]["client"]["file"]["id"]) + download_file(versiondata["logging"]["client"]["file"]["url"], logger_file, callback, + sha1=versiondata["logging"]["client"]["file"]["sha1"]) + # Download minecraft.jar + if "downloads" in versiondata: + download_file(versiondata["downloads"]["client"]["url"], + os.path.join(path, "versions", versiondata["id"], versiondata["id"] + ".jar"), callback, + sha1=versiondata["downloads"]["client"]["sha1"]) + # Need to copy jar for old forge versions + if not os.path.isfile(os.path.join(path, "versions", versiondata["id"], + versiondata["id"] + ".jar")) and "inheritsFrom" in versiondata: + inheritsFrom = versiondata["inheritsFrom"] + shutil.copyfile(os.path.join(path, "versions", versiondata["id"], versiondata["id"] + ".jar"), + os.path.join(path, "versions", inheritsFrom, inheritsFrom + ".jar")) + # Install java runtime if needed + if "javaVersion" in versiondata: + callback.get("setStatus", empty)("Install java runtime") + install_jvm_runtime(versiondata["javaVersion"]["component"], path, callback=callback) + + +def install_mc(versionid, minecraft_directory, callback=None): + """ + Install a Minecraft Version. Fore more Information take a look at the documentation" + """ + if isinstance(minecraft_directory, os.PathLike): + minecraft_directory = str(minecraft_directory) + if callback is None: + callback = {} + if os.path.isfile(os.path.join(minecraft_directory, "versions", versionid, f"{versionid}.json")): + do_version_install(versionid, minecraft_directory, callback) + return + version_list = requests.get("https://launchermeta.mojang.com/mc/game/version_manifest.json", + headers={"user-agent": get_user_agent()}).json() + for i in version_list["versions"]: + if i["id"] == versionid: + do_version_install(versionid, minecraft_directory, callback, url=i["url"]) + return + raise VersionNotFound(versionid) + + +def install_forge(versionid, path, callback=None): + if callback is None: + callback = {} + FORGE_DOWNLOAD_URL = \ + "https://files.minecraftforge.net/maven/net/minecraftforge/forge/{version}/forge-{version}-installer.jar" + temp_file_path = os.path.join(tempfile.gettempdir(), "forge-installer-" + str(random.randrange(1, 100000)) + ".tmp") + if not download_file(FORGE_DOWNLOAD_URL.format(version=versionid), temp_file_path, callback): + raise VersionNotFound(versionid) + zf = zipfile.ZipFile(temp_file_path, "r") + # Read the install_profile.json + with zf.open("install_profile.json", "r") as f: + version_content = f.read() + version_data = json.loads(version_content) + forge_version_id = version_data["version"] + t_mc = threading.Thread(target=install_mc, + args=(version_data["minecraft"], path, callback,)) + t_libs = ForgeThread(version_data, path, callback) + t_mc.start() + print("MC Başladı") + t_libs.start() + t_libs.any_signal.connect(t_libs.stop) + t_mc.join() + print("MC Join") + version_json_path = os.path.join(path, "versions", forge_version_id, forge_version_id + ".json") + extract_file(zf, "version.json", version_json_path) + # Extract forge libs from the installer + forge_lib_path = os.path.join(path, "libraries", "net", "minecraftforge", "forge", versionid) + try: + print("Extract file 1 başladı") + extract_file(zf, "maven/net/minecraftforge/forge/{version}/forge-{version}.jar".format(version=versionid), + os.path.join(forge_lib_path, "forge-" + versionid + ".jar")) + print("Extract file 1 bitti") + print("Extract file 2 başladı") + extract_file(zf, + "maven/net/minecraftforge/forge/{version}/forge-{version}-universal.jar".format(version=versionid), + os.path.join(forge_lib_path, "forge-" + versionid + "-universal.jar")) + print("Extract file 1 bitti") + except KeyError: + pass + # Extract the client.lzma + lzma_path = os.path.join(tempfile.gettempdir(), "lzma-" + str(random.randrange(1, 100000)) + ".tmp") + print("LZMA Başladı") + extract_file(zf, "data/client.lzma", lzma_path) + print("LZMA Bitti") + zf.close() + # Install the rest with the vanilla function + print("MC Tekrar Başladı") + install.install_minecraft_version(forge_version_id, path, callback=callback) + print("MC Bitti") + # Run the processors + print("Forge Processors") + forge.forge_processors(version_data, path, lzma_path, temp_file_path, callback) + # Delete the temporary files + os.remove(temp_file_path) + os.remove(lzma_path) class RunThread(QtCore.QThread): any_signal = QtCore.pyqtSignal() - def __init__(self, parent=None, mc_version="", forge_version="", mp_name=""): + def __init__(self, parent=None, mc_version="", forge_version="", mp_name="", username="MPDB_User", ram="2 GB"): super(RunThread, self).__init__(parent) self.mc_version = mc_version self.forge_version = forge_version self.mp_name = mp_name + self.username = username + self.ram = ram + print(username) + + def printProgressBar(self, iteration, total, prefix='', suffix='', decimals=1, length=100, fill='█', printEnd="\r"): + percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) + filledLength = int(length * iteration // total) + bar = fill * filledLength + '-' * (length - filledLength) + print('%s |%s| %s%% %s' % (prefix, bar, percent, suffix)) + # Print New Line on Complete + if iteration == total: + print() + def maximum(self, max_value, value): + max_value[0] = value def run(self): print(f"Running {self.forge_version} for {self.mc_version}") - forge_version = forge.find_forge_version(self.mc_version).split("-")[0] + "-forge-" + forge.find_forge_version(self.mc_version).split("-")[1] - forge.install_forge_version(forge.find_forge_version(self.mc_version), f"{self.mp_name}") - call(command.get_minecraft_command(forge_version, f"{self.mp_name}", {"username" : "MPDB"})) - - + max_value = [0] + callback = { + "setStatus": lambda text: print(text), + "setProgress": lambda value: self.printProgressBar(value, max_value[0]), + "setMax": lambda value: self.maximum(max_value, value) + } + s = time.perf_counter() + install_forge(f"{self.mc_version}-{self.forge_version}", path=self.mp_name, callback=callback) + print("Forge inme süresi: " + str(time.perf_counter() - s)) + options = { + "username": self.username, + "jvmArguments": [f"-Xmx{self.ram.replace(' GB', 'G')}"] + } + forge_ver = self.mc_version + "-forge-" + self.forge_version + print(forge_ver) + minecraft_command = get_minecraft_command(forge_ver, self.mp_name, options) + subprocess.call(minecraft_command) + self.any_signal.emit() + def stop(self): + print("Stopping") + self.wait() diff --git a/ui/forge_selector.py b/ui/forge_selector.py index 560474b..1db4277 100644 --- a/ui/forge_selector.py +++ b/ui/forge_selector.py @@ -8,7 +8,7 @@ def init(cls, values): cls.forge_selector = QComboBox(cls) cls.forge_selector.move(cls.modpack_selector.width(), 70) cls.forge_selector.setStyleSheet("background:rgba(185,187,190,255)") - info = loads(get("https://mpdb.xyz/get_data.php?data=true&from=name&name=" + cls.modpack_selector.currentText()).text) + info = loads(get(f"https://mpdb.xyz/api/modpack.php?name={cls.modpack_selector.currentText()}").text) if cls.modpack_selector.currentText() != "": - cls.forge_selector.addItems(get_forges(info["version"])) + cls.forge_selector.addItems(get_forges(info["mp_version"])) cls.forge_selector.adjustSize() diff --git a/ui/progress_bar.py b/ui/progress_bar.py new file mode 100644 index 0000000..90876df --- /dev/null +++ b/ui/progress_bar.py @@ -0,0 +1,8 @@ +from PyQt5.QtWidgets import QProgressBar + + +def init(cls): + cls.progress_bar = QProgressBar(cls) + cls.progress_bar.setStyleSheet("background:rgba(185,187,190,255)") + cls.progress_bar.move(0, cls.height() - 15) + cls.progress_bar.resize(cls.width(), 15) diff --git a/web_request.py b/web_request.py index b29ea8d..1ef344b 100644 --- a/web_request.py +++ b/web_request.py @@ -3,6 +3,7 @@ def get_data(url, get_json=True): if get_json: + print(get(url).text) r = loads(get(url).text) else: r = get(url).text