Skip to content
This repository was archived by the owner on Aug 26, 2022. It is now read-only.

Commit b8aab98

Browse files
v0.11 (bug fixes)
1 parent 1828ddc commit b8aab98

File tree

3 files changed

+129
-82
lines changed

3 files changed

+129
-82
lines changed

APLauncher.pyw

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from PIL import ImageTk, Image
66
from urllib.request import urlretrieve
77
from urllib.error import *
88
from zipfile import ZipFile
9+
import tkinter
910
import os
1011
import json
1112
import uuid
@@ -27,8 +28,10 @@ import logging
2728
import socket
2829
import random
2930
import platform
31+
import psutil
32+
import math
3033

31-
VERSION = "0.10"
34+
VERSION = "0.11"
3235

3336
def send_error_report(prog, fatal=False):
3437
try:
@@ -45,7 +48,7 @@ def send_error_report(prog, fatal=False):
4548
if messagebox.askyesno("Report issue", f"Would you like to automatically open an issue?"):
4649
newline = "\n"
4750
body = f"(insert description of bug here)%0A***%0ALog contents:%0A```%0A{tb.replace(newline, '%0A')}%0A```"
48-
url = f"https://github.com/SpacePython12/AP-Launcher/issues/new?"
51+
url = f"https://github.com/SpacePython12/AP-Launcher/issues/new?body={body}"
4952
webbrowser.open(url)
5053

5154
class AboutPage(Frame):
@@ -124,7 +127,7 @@ class App:
124127
os.remove("launcher_process_old.exe")
125128
logger.info("Update files cleaned up.")
126129
except:
127-
logger.info("No update files found")
130+
logger.info("No update files found.")
128131
pass
129132
logger.info("Downloading background and icon...")
130133
try:
@@ -143,24 +146,28 @@ class App:
143146
self.win.iconphoto(True, self.icon)
144147
self.background2 = Canvas(self.mainframe, width=self.background.width(), height=self.background.height())
145148
self.background2.grid(column=0, row=0, sticky="nsew")
146-
self.win.bind("<Configure>", lambda e: threading.Thread(None, target=lambda: self.resize_widgets(e)).start())
149+
self.background2.bind("<Configure>", lambda e: threading.Thread(None, target=lambda: self.resize_widgets()).start())
147150
self.versionvar = StringVar()
148151
self.get_versions()
149152
logger.info("Reading cache file...")
150153
try:
151154
self.cache = json.load(open("cache.json"))
155+
self.cache["launcherVersion"] = VERSION
156+
json.dump(self.cache, open("cache.json", "w"))
152157
logger.info("Successfully read cache.")
153158
except FileNotFoundError:
154159
self.cache = {
160+
"launcherVersion": VERSION,
155161
"username": "",
156162
"accessid": {
157163
"id": None,
158164
"expiresAt": None
159165
},
160-
"premium": True,
166+
"premium": False,
161167
"selectedVersion": None
162168
}
163169
logger.info("No cache file found.")
170+
json.dump(self.cache, open("cache.json", "w"))
164171
if type(self.cache["selectedVersion"]) is type(list()):
165172
self.versionvar.set(f'{self.cache["selectedVersion"][0]} ({self.cache["selectedVersion"][1]})')
166173
elif type(self.cache["selectedVersion"]) is type(None):
@@ -233,27 +240,57 @@ class App:
233240
java_home = os.path.join(os.getcwd(), "java", [x[1] for x in os.walk("java")][0][0])
234241
self.profjavadir = LabeledEntry(self.profileframe, "Java Directory: ", f'{java_home}\\bin\\javaw.exe', elength=30)
235242
self.profjavadir.grid(column=0, row=3, sticky="nsew")
236-
self.profjavargs = LabeledEntry(self.profileframe, "JVM Arguments: ", "-Xmx2G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M", elength=50)
237-
self.profjavargs.grid(column=0, row=4, sticky="nsew")
243+
self.jvmargs = "-Xmx2048M"
244+
self.allocramraw = re.search(r"\-Xmx[0-9]+[MGT]", self.jvmargs).group(0)[4:]
245+
if self.allocramraw[-1] == "M":
246+
self.scale = 0
247+
elif self.allocramraw[-1] == "G":
248+
self.scale = 1
249+
elif self.allocramraw[-1] == "T":
250+
self.scale = 2
251+
self.allocram = DoubleVar(self.win)
252+
self.allocram.set((float(self.allocramraw[:-1]) * (1024**(self.scale-1))))
253+
self.allocramlabel = Label(self.profileframe, text=f"Allocated RAM: {self.allocram.get()} GB")
254+
self.allocramlabel.grid(column=0, row=4, sticky="nsew")
255+
self.allocramslider = tkinter.Scale(self.profileframe, from_=1.0 * (1024**(self.scale)), to=int((math.ceil(math.log(psutil.virtual_memory().total, 1024)))) * (1024**(self.scale)), resolution=0.1, showvalue=False, orient="horizontal", variable=self.allocram, command=self.update_alloc_ram, length=200)
256+
self.allocramslider.grid(column=0, row=5, sticky="nw")
238257
self.profsave = Button(self.profileframe, text="Save")
239-
self.profsave.grid(column=0, row=5, sticky="w")
258+
self.profsave.grid(column=0, row=6, sticky="w")
240259
self.proftrans = Label(self.profileframe, text="OR")
241-
self.proftrans.grid(column=0, row=6, sticky="w")
260+
self.proftrans.grid(column=0, row=7, sticky="w")
242261
self.profadd = Button(self.profileframe, text="Import new version", command=lambda: self.open_install_archive())
243-
self.profadd.grid(column=0, row=7, sticky="w")
262+
self.profadd.grid(column=0, row=8, sticky="w")
244263
self.update_profiles(self.versionvar.get())
245264
self.profilelist.bind("<<ComboboxSelected>>", lambda x: self.update_profiles(self.versionvar.get()))
246265
self.aboutpage = AboutPage(self.win)
247266
self.aboutpage.columnconfigure(0, weight=1)
248267
self.tabs.add(self.aboutpage, text="About")
249268
logger.info("Successfully initiated window.")
250269

251-
def resize_widgets(self, e, override=False):
252-
if ((self.win.winfo_width() != e.width) and (self.win.winfo_height() != e.height)) or override:
253-
self.bgfile = Image.open("assets/background.png")
254-
self.bgfile = self.bgfile.resize((e.width, e.height), Image.ANTIALIAS)
255-
self.background = ImageTk.PhotoImage(self.bgfile)
256-
self.background2.create_image(0, 0, image=self.background, anchor="nw")
270+
def update_alloc_ram(self, e):
271+
self.allocramlabel.config(text=f"Allocated RAM: {self.allocram.get()/(1024**(self.scale))} GB")
272+
oldarg = re.search(r"\-Xmx[0-9]+[MGT]", self.jvmargs).group(0)
273+
if self.scale == 0:
274+
suffix = "M"
275+
elif self.scale == 1:
276+
suffix = "G"
277+
elif self.scale == 2:
278+
suffix = "T"
279+
if self.allocram.get()/(1024**(self.scale-1)) % 1024 == 0:
280+
if suffix == "M":
281+
suffix = "G"
282+
elif suffix == "G":
283+
suffix = "T"
284+
self.jvmargs = self.jvmargs.replace(oldarg, f"-Xmx{int(self.allocram.get()/(1024**(self.scale)))}{suffix}")
285+
else:
286+
self.jvmargs = self.jvmargs.replace(oldarg, f"-Xmx{int(self.allocram.get()/(1024**(self.scale-1)))}{suffix}")
287+
288+
def resize_widgets(self):
289+
self.bgfile = Image.open("assets/background.png")
290+
self.bgfile = self.bgfile.resize((self.background2.winfo_width(), self.background2.winfo_height()), Image.ANTIALIAS)
291+
self.background = ImageTk.PhotoImage(self.bgfile)
292+
self.background2.create_image(0, 0, image=self.background, anchor="nw")
293+
257294

258295
def kill_process(self):
259296
"""Kills the running Minecraft process."""
@@ -330,14 +367,10 @@ class App:
330367
self.profjavadir.set(self.accounts["profiles"][self.nametoprofile[name]]["javaDir"])
331368
except:
332369
pass
333-
try:
334-
self.profjavargs.set(self.accounts["profiles"][self.nametoprofile[name]]["javaArgs"])
335-
except:
336-
pass
337370

338371
def save_profile(self, name):
339372
"""Saves the selected profile."""
340-
self.accounts["profiles"][self.nametoprofile[name]] = {"name": self.profname.get(), "type": "custom", "lastVersionId": self.accounts["profiles"][self.nametoprofile[name]]["lastVersionId"], "gameDir": self.profgamedir.get(), "javaDir": self.profjavadir.get(), "javaArgs": self.profjavargs.get()}
373+
self.accounts["profiles"][self.nametoprofile[name]] = {"name": self.profname.get(), "type": "custom", "lastVersionId": self.accounts["profiles"][self.nametoprofile[name]]["lastVersionId"], "gameDir": self.profgamedir.get(), "javaDir": self.profjavadir.get(), "javaArgs": self.jvmargs}
341374
self.get_versions()
342375
self.versionlist["values"] = self.versions
343376
self.profilelist["values"] = self.versions
@@ -404,7 +437,7 @@ class App:
404437
"-javaHome",
405438
self.profjavadir.get(),
406439
"-javaArgs",
407-
self.profjavargs.get(),
440+
self.jvmargs,
408441
"-launcherServerSocket",
409442
str(sock)
410443
]
@@ -598,9 +631,7 @@ if __name__ == "__main__":
598631
except BaseException as e:
599632
messagebox.showerror("Fatal error", "A fatal error has occurred during startup.")
600633
logger.error(e, exc_info=True)
601-
with open("launcher_logs/error.log", "w") as err:
602-
err.write(sys.exc_info())
603-
err.close()
634+
traceback.print_exc(file=open("launcher_logs/error.log", "w"))
604635
send_error_report("gui", fatal=True)
605636
sys.exit()
606637
try:
@@ -609,9 +640,7 @@ if __name__ == "__main__":
609640
if not type(e).__name__ == "SystemExit":
610641
messagebox.showerror("Fatal error", "A fatal error has occurred during runtime.")
611642
logger.error(e, exc_info=True)
612-
with open("launcher_logs/error.log", "w") as err:
613-
err.write(sys.exc_info())
614-
err.close()
643+
traceback.print_exc(file=open("launcher_logs/error.log", "w"))
615644
send_error_report("gui", fatal=True)
616645
main.win.destroy()
617646
sys.exit()

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# AP-Launcher [![downloads](https://img.shields.io/github/downloads/SpacePython12/AP-Launcher/total.svg)](https://github.com/SpacePython12/AP-Launcher/releases/latest)
1+
# AP-Launcher
22
A custom Minecraft launcher coded entirely in python.
33

44
* I am NOT responsible for any punishments given out by your network administrator for downloading this software.
@@ -13,10 +13,9 @@ You may need to install some libraries, including...
1313
* Premium accounts are NOT supported.
1414

1515
# Changelog:
16-
* Fullscreen now added (may be buggy)
17-
* Fixed the updater (probably still broken...)
18-
* Updated version file format
19-
* Re-added the ability to kill the Minecraft process
16+
* Fullscreen resize bug is fixed.
17+
* Increased compatibility with modded versions.
18+
* Added a slider for allocated RAM.
2019

2120
# Installation instructions:
2221

@@ -34,4 +33,4 @@ You may need to install some libraries, including...
3433
5. Restart AP Launcher and select the version you just downloaded.
3534
6. Click "Play".
3635
7. You should be able to play Minecraft now!
37-
- Have fun!
36+
- Have fun!

launcher_process.py

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import datetime
1313
import logging
1414
import socket
15+
import requests
16+
import traceback
1517

1618
# Base program derived from https://stackoverflow.com/questions/14531917/launch-minecraft-from-command-line-username-and-password-as-prefix
1719

@@ -219,8 +221,6 @@ def move_natives(mcdir, nativesdir):
219221
clientJson = json.load(
220222
open(os.path.join(mcDir, 'versions', inheritor, f'{inheritor}.json'))
221223
)
222-
gameArgs.extend(clientJson2["arguments"]["game"])
223-
gameArgs.extend(clientJson2["arguments"]["jvm"])
224224
clientJson["libraries"] = clientJson2["libraries"] + clientJson["libraries"]
225225
clientJson["id"] = clientJson2["id"]
226226
clientJson["inheritsFrom"] = clientJson2["inheritsFrom"]
@@ -247,63 +247,84 @@ def move_natives(mcdir, nativesdir):
247247
mainClass = clientJson['mainClass']
248248
else:
249249
mainClass = clientJson2['mainClass']
250+
try:
251+
clientJson["arguments"]["jvm"] = clientJson2["arguments"]["jvm"] + clientJson["arguments"]["jvm"]
252+
except:
253+
pass
254+
try:
255+
clientJson["arguments"]["game"].extend(clientJson2["arguments"]["game"])
256+
except:
257+
pass
258+
250259
versionType = clientJson['type']
251260
authDatabase = accountJson["accounts"]
252261
logger.info("Checking for UUID...")
253-
uuid = str(uuidlib.uuid4())
254-
for key in authDatabase:
255-
if authDatabase[key]["minecraftProfile"]["name"] == username:
256-
uuid = authDatabase[key]["minecraftProfile"]["id"]
257-
break
262+
uuid = None
263+
try:
264+
uuid = requests.get(f"https://mc-heads.net/minecraft/profile/{username}").json()["id"]
265+
except:
266+
for key in authDatabase:
267+
if authDatabase[key]["minecraftProfile"]["name"] == username:
268+
uuid = authDatabase[key]["minecraftProfile"]["id"]
269+
break
270+
if uuid is None:
271+
logger.error("Invalid and/or corrupt profile detected.")
272+
sys.exit()
258273

259274
logger.info("Debugging...")
260275
debug(classPath)
261276
debug(mainClass)
262277
debug(versionType)
263278
debug(assetIndex)
264279

265-
argFilePath = "C://Temp/arguments.txt"
280+
argFilePath = "C://Temp/classpath.txt"
266281

267282
with open(argFilePath, "w") as f:
268283
f.write(classPath)
269284
f.close()
270-
finalJvmArgs = [
271-
"title",
272-
"APLauncher_process",
273-
"&&",
274-
javaHome,
275-
f'-Djava.library.path={nativesDir}',
276-
f'-Djava-args={javaArgs}',
277-
'-Dminecraft.launcher.brand=custom-launcher',
278-
'-Dminecraft.launcher.version=2.1',
279-
'-Dorg.lwjgl.util.DebugLoader=true',
280-
'-cp',
281-
f'@{argFilePath}',
282-
mainClass
283-
]
284-
finalJvmArgs = finalJvmArgs + ([i.replace("${library_directory}", os.path.join(mcDir, "libraries")).replace("${classpath_separator}", ";") for i in jvmArgs])
285-
finalGameArgs = [
286-
'--username',
287-
username,
288-
'--version',
289-
version,
290-
'--gameDir',
291-
mcDir,
292-
'--assetsDir',
293-
assetsDir,
294-
'--assetIndex',
295-
assetIndex,
296-
'--uuid',
297-
uuid,
298-
'--accessToken',
299-
accessToken,
300-
'--userType',
301-
accountType,
302-
'--versionType',
303-
'release'
304-
]
305-
finalArgs = finalJvmArgs + finalGameArgs
306-
finalArgs = finalArgs + gameArgs
285+
286+
clientJson["arguments"]["jvm"].append(mainClass)
287+
preJvmArgs = []
288+
for index in range(len(clientJson["arguments"]["jvm"])):
289+
if isinstance(clientJson["arguments"]["jvm"][index], dict):
290+
should_use = should_use_library(clientJson["arguments"]["jvm"][index])
291+
if should_use:
292+
if isinstance(clientJson["arguments"]["jvm"][index]["value"], list):
293+
preJvmArgs.extend(clientJson["arguments"]["jvm"][index]["value"])
294+
elif isinstance(clientJson["arguments"]["jvm"][index]["value"], str):
295+
preJvmArgs.append(clientJson["arguments"]["jvm"][index]["value"])
296+
else:
297+
preJvmArgs.append(clientJson["arguments"]["jvm"][index])
298+
preGameArgs = []
299+
for index in range(len(clientJson["arguments"]["game"])):
300+
if isinstance(clientJson["arguments"]["game"][index], dict):
301+
pass
302+
else:
303+
preGameArgs.append(clientJson["arguments"]["game"][index])
304+
305+
306+
launchVars = {
307+
"auth_player_name": username,
308+
"version_name": version,
309+
"game_directory": mcDir,
310+
"assets_root": assetsDir,
311+
"assets_index_name": assetIndex,
312+
"auth_uuid": uuid,
313+
"auth_access_token": accessToken,
314+
"user_type": "mojang",
315+
"version_type": clientJson["type"],
316+
"natives_directory": nativesDir,
317+
"launcher_name": "AP-Launcher",
318+
"launcher_version": json.load(open("cache.json"))["launcherVersion"],
319+
"library_directory": os.path.join(mcDir, "libraries"),
320+
"classpath_separator": os.pathsep,
321+
"classpath": f'@{argFilePath}'
322+
}
323+
324+
argList = javaArgs.split() + preJvmArgs + preGameArgs
325+
argList.insert(0, javaHome)
326+
finalArgs = [arg.replace("$", "").format(**launchVars) for arg in argList]
327+
307328
logger.info("Running the game...")
308329
sb = subprocess.Popen(finalArgs,
309330
shell=True,
@@ -320,9 +341,7 @@ def move_natives(mcdir, nativesdir):
320341
except BaseException as e:
321342
if not type(e).__name__ == "SystemExit":
322343
logger.error(e, exc_info=True)
323-
with open("launcher_logs/error.log", "w") as err:
324-
err.write(sys.exc_info())
325-
err.close()
344+
traceback.print_exc(file=open("launcher_logs/error.log", "w"))
326345
launcherClient.send(b"\xff")
327346
else:
328347
launcherClient.send(b"\x00")

0 commit comments

Comments
 (0)