2024-06-20 08:30:57 -06:00
|
|
|
from ftplib import FTP_TLS as FTP
|
|
|
|
import io
|
|
|
|
import json
|
|
|
|
import tempfile
|
|
|
|
import subprocess
|
|
|
|
import time
|
|
|
|
import os
|
|
|
|
import datetime
|
2024-06-20 12:07:56 -06:00
|
|
|
import zipfile
|
2024-06-20 16:09:18 -06:00
|
|
|
from customtkinter import *
|
|
|
|
import time
|
|
|
|
import math
|
2024-06-20 21:24:17 -06:00
|
|
|
import tarfile
|
|
|
|
from pathlib import Path
|
2024-06-20 22:26:30 -06:00
|
|
|
import shutil
|
2024-06-20 21:24:17 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_tar_members_stripped(tar, n_folders_stripped = 1):
|
|
|
|
members = []
|
|
|
|
for member in tar.getmembers():
|
|
|
|
p = Path(member.path)
|
|
|
|
member.path = p.relative_to(*p.parts[:n_folders_stripped])
|
|
|
|
members.append(member)
|
|
|
|
return members
|
|
|
|
|
2024-06-20 22:26:30 -06:00
|
|
|
def unzip(zip, dest):
|
|
|
|
for file_info in zip.infolist():
|
|
|
|
if file_info.is_dir():
|
|
|
|
continue
|
|
|
|
|
|
|
|
file_path = file_info.filename
|
|
|
|
|
|
|
|
extracted_path = file_path.split("/", 1)[1]
|
|
|
|
extracted_path = os.path.join(dest, extracted_path)
|
|
|
|
print(extracted_path)
|
|
|
|
os.makedirs(os.path.dirname(extracted_path), exist_ok=True)
|
|
|
|
with open(extracted_path, 'wb') as dst:
|
|
|
|
with zip.open(file_info, 'r') as src:
|
|
|
|
shutil.copyfileobj(src, dst)
|
|
|
|
|
|
|
|
|
2024-06-20 21:24:17 -06:00
|
|
|
def install_java(mc_version):
|
|
|
|
global data_dir
|
|
|
|
tokens = mc_version.split(".")
|
|
|
|
java = 8
|
|
|
|
if tokens[1] == "17":
|
|
|
|
java = 17
|
|
|
|
elif tokens[1] == "18" or tokens[1] == "19":
|
|
|
|
java = 17
|
|
|
|
elif int(tokens[1]) > 19:
|
|
|
|
if tokens[1] == "20" and int(tokens[2]) < 5:
|
|
|
|
java = 17
|
|
|
|
else:
|
|
|
|
java = 21
|
|
|
|
|
|
|
|
suff = "win"
|
|
|
|
ext = ".zip"
|
|
|
|
if os.name == "posix":
|
|
|
|
suff = "lin"
|
|
|
|
ext = ".tar.gz"
|
|
|
|
|
|
|
|
java_path = "java/java-"+str(java)+"-"+suff+ext
|
|
|
|
if not os.path.exists(data_dir+"/java/java-"+str(java)+"-"+suff):
|
|
|
|
print("Downloading Java "+str(java))
|
|
|
|
ftp = FTP("gitea.piwalker.net")
|
|
|
|
ftp.login()
|
|
|
|
ftp.prot_p()
|
|
|
|
with io.BytesIO() as buff:
|
|
|
|
ftp.retrbinary("RETR "+java_path,buff.write)
|
|
|
|
if os.name == "posix":
|
|
|
|
buff.seek(0)
|
|
|
|
with tarfile.open(fileobj=buff) as tar:
|
|
|
|
print(tar.getnames())
|
|
|
|
tar.extractall(data_dir+"/java/java-"+str(java)+"-"+suff, members=get_tar_members_stripped(tar, 1))
|
2024-06-20 22:26:30 -06:00
|
|
|
else:
|
|
|
|
with zipfile.ZipFile(buff) as zip:
|
|
|
|
unzip(zip, data_dir+"/java/java-"+str(java)+"-"+suff)
|
|
|
|
|
|
|
|
|
2024-06-20 21:24:17 -06:00
|
|
|
return java
|
|
|
|
|
2024-06-20 16:09:18 -06:00
|
|
|
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 17:47:35 -06:00
|
|
|
def resource_path(relative_path):
|
|
|
|
try:
|
|
|
|
base_path = sys._MEIPASS
|
|
|
|
except Exception:
|
|
|
|
base_path = os.path.abspath(".")
|
|
|
|
return os.path.join(base_path, relative_path)
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 12:07:56 -06:00
|
|
|
def perform_installation(instance_name, prism_command, prism_instance_path, pack):
|
2024-06-20 21:24:17 -06:00
|
|
|
global data_dir
|
|
|
|
if os.name == "posix":
|
|
|
|
data_dir = os.getenv("HOME") + "/.local/share/FCLauncher"
|
|
|
|
else:
|
|
|
|
data_dir = os.getenv("APPDATA") + "/FCLauncher"
|
|
|
|
|
|
|
|
Path(data_dir).mkdir(parents=True, exist_ok=True)
|
|
|
|
Path(data_dir+"/java").mkdir(parents=True, exist_ok=True)
|
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
try:
|
|
|
|
ftp = FTP("gitea.piwalker.net")
|
|
|
|
ftp.login()
|
|
|
|
ftp.prot_p()
|
2024-06-20 12:07:56 -06:00
|
|
|
ftp.cwd(pack)
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
# Fetching versions.json from FTP
|
|
|
|
bio = io.BytesIO()
|
|
|
|
ftp.retrbinary("RETR versions.json", bio.write)
|
|
|
|
bio.seek(0)
|
|
|
|
versions = json.load(bio)
|
|
|
|
bio.close()
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
# Checking current version
|
|
|
|
version = "0.0.0"
|
|
|
|
version_file_path = os.path.join(prism_instance_path, instance_name, ".minecraft", "version.txt")
|
|
|
|
if os.path.exists(version_file_path):
|
|
|
|
with open(version_file_path, 'r') as fp:
|
|
|
|
version = fp.readline().rstrip()
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
# Checking if update is needed
|
|
|
|
if version != versions[-1]["Version"]:
|
|
|
|
print(f"Current version: {version}")
|
|
|
|
print(f"Latest version: {versions[-1]['Version']}")
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
# Downloading modpack
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
modpack_file_path = os.path.join(temp_dir, instance_name + ".mrpack")
|
|
|
|
with open(modpack_file_path, 'wb') as modpack:
|
2024-06-20 16:09:18 -06:00
|
|
|
ftpDownload(ftp, versions[-1]["File"], modpack)
|
2024-06-20 08:48:58 -06:00
|
|
|
|
|
|
|
# Running PrismLauncher with modpack
|
|
|
|
subprocess.Popen([prism_command, '-I', modpack_file_path])
|
2024-06-20 08:30:57 -06:00
|
|
|
|
2024-06-20 08:48:58 -06:00
|
|
|
# Waiting for installation to complete
|
|
|
|
while True:
|
|
|
|
time.sleep(5)
|
|
|
|
if os.path.exists(version_file_path):
|
|
|
|
with open(version_file_path, 'r') as fp:
|
|
|
|
version = fp.readline().rstrip()
|
|
|
|
if version == versions[-1]["Version"]:
|
2024-06-20 21:24:17 -06:00
|
|
|
info_file_path = os.path.join(prism_instance_path, instance_name, "mmc-pack.json")
|
|
|
|
mc_version = ""
|
|
|
|
with open(info_file_path) as file:
|
|
|
|
info = json.load(file)
|
|
|
|
for component in info["components"]:
|
|
|
|
if component["uid"] == "net.minecraft":
|
|
|
|
mc_version = component["version"]
|
|
|
|
break;
|
|
|
|
java = install_java(mc_version)
|
|
|
|
option_path = os.path.join(prism_instance_path, instance_name, "instance.cfg")
|
|
|
|
data = []
|
|
|
|
suff = "win"
|
2024-06-20 22:26:30 -06:00
|
|
|
cmd = "java.exe"
|
2024-06-20 21:24:17 -06:00
|
|
|
if os.name == "posix":
|
|
|
|
suff = "lin"
|
2024-06-20 22:26:30 -06:00
|
|
|
cmd = "java"
|
2024-06-20 21:24:17 -06:00
|
|
|
with open(option_path, 'r') as file:
|
|
|
|
data = file.readlines()
|
|
|
|
was_set = False
|
|
|
|
for ind, line in enumerate(data):
|
|
|
|
if line.startswith("JavaPath="):
|
2024-06-20 22:26:30 -06:00
|
|
|
data[ind] = "JavaPath="+data_dir.replace("\\", "/")+"/java/java-"+str(java)+"-"+suff+"/bin/"+cmd+"\n"
|
2024-06-20 21:24:17 -06:00
|
|
|
was_set = True
|
|
|
|
if not was_set:
|
2024-06-20 22:26:30 -06:00
|
|
|
data.append("JavaPath="+data_dir.replace("\\", "/")+"/java/java-"+str(java)+"-"+suff+"/bin/"+cmd+"\n")
|
2024-06-20 21:24:17 -06:00
|
|
|
data.append("OverrideJavaLocation=true\n")
|
|
|
|
data.append("OverrideJava=true\n")
|
|
|
|
with open(option_path, 'w') as file:
|
|
|
|
file.writelines(data)
|
|
|
|
print(data)
|
|
|
|
|
|
|
|
|
|
|
|
subprocess.run([prism_command, '-l', instance_name])
|
|
|
|
time.sleep(30)
|
2024-06-20 08:48:58 -06:00
|
|
|
break
|
2024-06-20 21:24:17 -06:00
|
|
|
else:
|
|
|
|
subprocess.run([prism_command, '-l', instance_name])
|
2024-06-20 16:09:18 -06:00
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
2024-06-20 08:48:58 -06:00
|
|
|
print("Unable to check for updates. Modpack may be out of date")
|
|
|
|
subprocess.run([prism_command, '-l', instance_name])
|
2024-06-20 08:30:57 -06:00
|
|
|
|
|
|
|
|
2024-06-20 12:07:56 -06:00
|
|
|
def upload_pack(username, password, version_tag, fileName, pack):
|
2024-06-20 08:30:57 -06:00
|
|
|
ftp = FTP("gitea.piwalker.net", username, password)
|
|
|
|
ftp.prot_p()
|
2024-06-20 12:07:56 -06:00
|
|
|
ftp.cwd(pack)
|
2024-06-20 08:30:57 -06:00
|
|
|
bio = io.BytesIO()
|
|
|
|
ftp.retrbinary("RETR versions.json", bio.write)
|
|
|
|
bio.seek(0)
|
|
|
|
versions = json.load(bio)
|
|
|
|
bio.close()
|
|
|
|
time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
2024-06-20 12:07:56 -06:00
|
|
|
versions.append({"Version":version_tag, "Date":time, "File":"Versions/"+pack+time+".mrpack"})
|
|
|
|
with zipfile.ZipFile(fileName, 'r') as zin:
|
|
|
|
zipbytes = io.BytesIO()
|
|
|
|
zout = zipfile.ZipFile(zipbytes, 'w')
|
|
|
|
for item in zin.infolist():
|
|
|
|
buffer = zin.read(item.filename)
|
|
|
|
if item.filename != "overrides/version.txt":
|
|
|
|
zout.writestr(item, buffer)
|
|
|
|
zout.writestr("overrides/version.txt", version_tag)
|
|
|
|
zout.close()
|
|
|
|
zipbytes.seek(0)
|
|
|
|
ftp.storbinary("STOR "+versions[len(versions)-1]["File"], zipbytes)
|
2024-06-20 08:30:57 -06:00
|
|
|
bio = io.BytesIO()
|
|
|
|
bio.write(json.dumps(versions).encode())
|
|
|
|
bio.seek(0)
|
|
|
|
ftp.storbinary("STOR versions.json", bio)
|
|
|
|
bio.close()
|
2024-06-20 12:07:56 -06:00
|
|
|
modpackUpdate(pack, ftp)
|
2024-06-20 08:30:57 -06:00
|
|
|
ftp.close()
|
|
|
|
|
2024-06-20 12:07:56 -06:00
|
|
|
def getModpacks():
|
|
|
|
ftp = FTP("gitea.piwalker.net")
|
|
|
|
ftp.login()
|
|
|
|
ftp.prot_p()
|
|
|
|
bio = io.BytesIO()
|
|
|
|
ftp.retrbinary("RETR modpacks.json", bio.write)
|
|
|
|
bio.seek(0)
|
|
|
|
ftp.close()
|
|
|
|
return json.load(bio)
|
|
|
|
|
|
|
|
def uploadModpacks(modpacks, ftp):
|
|
|
|
ftp.cwd("/ftp");
|
|
|
|
bio = io.BytesIO()
|
|
|
|
bio.write(json.dumps(modpacks).encode())
|
|
|
|
bio.seek(0)
|
|
|
|
ftp.storbinary("STOR modpacks.json", bio)
|
|
|
|
bio.close()
|
|
|
|
|
|
|
|
def modpackUpdate(id, ftp):
|
|
|
|
modpacks = getModpacks()
|
|
|
|
time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
|
|
for modpack in modpacks:
|
|
|
|
if modpack["id"] == id:
|
|
|
|
modpack["last-updated"] = time
|
|
|
|
uploadModpacks(modpacks, ftp);
|
|
|
|
|
|
|
|
def createModpack(id, name, username, password):
|
|
|
|
modpacks = getModpacks()
|
|
|
|
time = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
|
|
modpacks.append({"id":id, "name":name, "last-updated":time})
|
|
|
|
ftp = FTP("gitea.piwalker.net", username, password)
|
|
|
|
ftp.prot_p()
|
|
|
|
uploadModpacks(modpacks,ftp)
|
|
|
|
ftp.mkd(id)
|
|
|
|
ftp.cwd(id)
|
|
|
|
versions = []
|
|
|
|
bio = io.BytesIO()
|
|
|
|
bio.write(json.dumps(versions).encode())
|
|
|
|
bio.seek(0)
|
|
|
|
ftp.storbinary("STOR versions.json", bio)
|
|
|
|
ftp.mkd("Versions")
|
|
|
|
ftp.close()
|
|
|
|
|
2024-06-20 16:09:18 -06:00
|
|
|
def deleteModpack(username, password, id):
|
|
|
|
ftp = FTP("gitea.piwalker.net", username, password)
|
|
|
|
ftp.prot_p()
|
|
|
|
modpacks = getModpacks()
|
|
|
|
for pack in modpacks:
|
|
|
|
if pack["id"] == id:
|
|
|
|
modpacks.remove(pack)
|
|
|
|
break
|
|
|
|
uploadModpacks(modpacks, ftp)
|
|
|
|
deleteFolder(ftp, id)
|
|
|
|
|
|
|
|
def deleteFolder(ftp, path):
|
|
|
|
print("Deleting folder: "+path)
|
|
|
|
ftp.cwd(path)
|
|
|
|
for item in ftp.nlst():
|
|
|
|
try:
|
|
|
|
print("deleting file: "+path)
|
|
|
|
ftp.delete(item)
|
|
|
|
except:
|
|
|
|
deleteFolder(ftp, item)
|
|
|
|
ftp.cwd("..")
|
|
|
|
ftp.rmd(path)
|
|
|
|
|
|
|
|
def ftpDownload(ftp, file, stream):
|
|
|
|
#create tkinter window
|
|
|
|
print("downloading file: "+file)
|
|
|
|
dialog = CTk()
|
2024-06-20 18:50:01 -06:00
|
|
|
if os.name == 'posix':
|
|
|
|
dialog.attributes('-type', 'dialog')
|
2024-06-20 16:09:18 -06:00
|
|
|
dialog.title("Downloading Modpack")
|
|
|
|
set_appearance_mode("dark")
|
|
|
|
set_default_color_theme("blue")
|
2024-06-20 12:07:56 -06:00
|
|
|
|
2024-06-20 16:09:18 -06:00
|
|
|
global pbar
|
|
|
|
pbar = CTkProgressBar(master=dialog)
|
|
|
|
pbar.pack(padx=20, pady=20)
|
|
|
|
global progress
|
|
|
|
global ETA
|
|
|
|
progress = StringVar()
|
|
|
|
ETA = StringVar()
|
|
|
|
progress_label = CTkLabel(master=dialog, textvariable=progress)
|
|
|
|
progress_label.pack()
|
|
|
|
eta_label = CTkLabel(master=dialog, textvariable=ETA)
|
|
|
|
eta_label.pack()
|
|
|
|
size = ftp.size(file)
|
|
|
|
global total
|
|
|
|
total = 0
|
|
|
|
global start
|
|
|
|
global timer
|
|
|
|
timer = 0
|
|
|
|
start = time.time()
|
|
|
|
dialog.update()
|
2024-06-20 12:07:56 -06:00
|
|
|
|
2024-06-20 16:09:18 -06:00
|
|
|
def downloadCallback(data):
|
|
|
|
global total
|
|
|
|
global pbar
|
|
|
|
global start
|
|
|
|
global progress
|
|
|
|
global ETA
|
|
|
|
global timer
|
|
|
|
stream.write(data)
|
|
|
|
total += len(data)
|
|
|
|
if time.time() - timer >= 1:
|
|
|
|
progress.set(str(round(total/1048576, 1))+" MB / "+str(round(size/1048576, 1))+" MB @ " + str(round((total/1048576)/(time.time()-start), 3))+" MB/s")
|
|
|
|
time_left = (size-total)/(total/(time.time()-start))
|
|
|
|
ETA.set("ETA: " + str(datetime.timedelta(seconds=math.ceil(time_left))))
|
|
|
|
pbar.set(total/size)
|
|
|
|
timer = time.time()
|
2024-06-20 16:17:24 -06:00
|
|
|
dialog.update()
|
2024-06-20 16:09:18 -06:00
|
|
|
ftp.retrbinary("RETR " + file, downloadCallback)
|
2024-06-20 16:55:21 -06:00
|
|
|
dialog.destroy()
|
2024-06-20 12:07:56 -06:00
|
|
|
|