__main__
This commit is contained in:
parent
b2f7a7161b
commit
c152e511a0
455
__main__.py
Normal file
455
__main__.py
Normal file
@ -0,0 +1,455 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter import messagebox, font
|
||||
from ttkthemes import ThemedTk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
import json
|
||||
import webbrowser
|
||||
from subprocess import Popen, PIPE
|
||||
from pathlib import Path
|
||||
import threading
|
||||
from sys import platform
|
||||
import requests
|
||||
from hashlib import sha256
|
||||
from os import listdir
|
||||
from os.path import isfile, join, exists
|
||||
import shutil
|
||||
|
||||
logarr = []
|
||||
with open('lang.json') as f: lang=json.load(f)
|
||||
|
||||
class menus:
|
||||
def clientmenu(file, mainframe):
|
||||
for child in mainframe.winfo_children(): child.destroy()
|
||||
checkclient = str(helpers.check4client(helpers.getclientpath(file)))
|
||||
clientdetect = ttk.Label(mainframe, text=f"Was Bootstrapper found: {checkclient}", anchor=tk.W)
|
||||
clientdetect.grid(row=0, sticky=tk.W)
|
||||
#not threading this prob stalls the gui, but it's not like the server, where I actually need it to run in the background; in otherwords, I do not care :)
|
||||
dlldownload = ttk.Button(mainframe, text="Download Latest Bootstrapper", command=lambda: helpers.downloadlatestdll(helpers.getclientpath(file)))
|
||||
dlldownload.grid(row=1, sticky=tk.W)
|
||||
|
||||
#menu for pluto scripts; running them is not currently implemented
|
||||
def plutomenu(file, mainframe):
|
||||
for child in mainframe.winfo_children(): child.destroy()
|
||||
openwfpath = helpers.getclientpath(file) + "/OpenWF/scripts/"
|
||||
def openfolder(folder):
|
||||
Popen(['xdg-open', folder])
|
||||
|
||||
onlyfiles = [f for f in listdir(openwfpath) if isfile(join(openwfpath, f))]
|
||||
i = 0
|
||||
for file in onlyfiles:
|
||||
filelabel = ttk.Label(mainframe, text=file, anchor=tk.W)
|
||||
filelabel.grid(row = i, column = 0, sticky = tk.W, pady = 2)
|
||||
plutorun = ttk.Button(mainframe, text="Running not implemented yet", command=lambda: runpluto(file))
|
||||
plutorun.grid(row = i, column = 1, sticky = tk.W, pady = 2, padx=10)
|
||||
i += 1
|
||||
|
||||
# Input would be a json file
|
||||
def settingsmenu(file, mainframe, vcmd):
|
||||
|
||||
for child in mainframe.winfo_children(): child.destroy()
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
settings = json.load(f)
|
||||
i = 0
|
||||
frameunder = ttk.Frame(mainframe)
|
||||
tempsettings = {}
|
||||
print(settings)
|
||||
for key in settings.keys():
|
||||
if key in lang.keys():
|
||||
item = ttk.Label(frameunder, text=(lang[key] + " : "))
|
||||
else:
|
||||
item = ttk.Label(frameunder, text=(key + " : "))
|
||||
item.grid(row = i, column = 0, sticky = tk.W, pady = 2)
|
||||
print (type(settings[key]))
|
||||
if type(settings[key]) is str:
|
||||
value = ttk.Entry(frameunder, width=50)
|
||||
value.insert(0,settings[key])
|
||||
|
||||
elif type(settings[key]) not in [str, bool, int, dict]:
|
||||
value = ttk.Label(frameunder, text="N/A")
|
||||
elif type(settings[key]) is bool:
|
||||
boeol = tk.StringVar()
|
||||
value = ttk.OptionMenu(frameunder, boeol, settings[key], True, False)
|
||||
|
||||
elif type(settings[key]) is int:
|
||||
value = ttk.Entry(frameunder, width=50, validate='key', validatecommand=(vcmd, '%P'))
|
||||
value.insert(0,settings[key])
|
||||
#stupid ass nested dicts eat my whole balls
|
||||
elif type(settings[key]) is dict:
|
||||
value = {}
|
||||
for k in settings[key].keys():
|
||||
value[k] ={}
|
||||
if type(settings[key][k]) is str:
|
||||
value[k]["value"] = ttk.Entry(frameunder, width=50)
|
||||
value[k]["value"].insert(0,settings[key][k])
|
||||
elif type(settings[key][k]) not in [str, bool, int]:
|
||||
value[k]["value"] = ttk.Label(frameunder, text="N/A")
|
||||
elif type(settings[key][k]) is bool:
|
||||
boeol = tk.BooleanVar()
|
||||
value[k]["value"] = ttk.OptionMenu(frameunder, boeol, settings[key][k], True, False)
|
||||
boeol.set(settings[key][k])
|
||||
value[k]["valueop"] = boeol
|
||||
elif type(settings[key][k]) is int:
|
||||
value[k]["value"] = ttk.Entry(frameunder, width=50, validate='key', validatecommand=(vcmd, '%P'))
|
||||
value[k]["value"].insert(0,settings[key][k])
|
||||
value[k]["type"] = type(settings[key][k])
|
||||
|
||||
|
||||
if type(value) is not dict:
|
||||
value.grid(row = i, column = 1, sticky = tk.W, pady = 2)
|
||||
i += 1
|
||||
else:
|
||||
i+=1
|
||||
sp1=ttk.Separator(mainframe,orient='horizontal')
|
||||
sp1.grid(row=i,column=1,sticky='ew')
|
||||
i+=1
|
||||
for k in value.keys():
|
||||
print(k)
|
||||
dictlabel = ttk.Label(frameunder, text=(k + " : "))
|
||||
dictlabel.grid(row = i, column = 0, sticky = tk.E, pady = 2)
|
||||
value[k]["value"].grid(row = i, column = 1, sticky = tk.W, pady = 2)
|
||||
i+=1
|
||||
ttk.Separator(mainframe, orient=tk.HORIZONTAL).grid(row=i)
|
||||
i+=1
|
||||
if type(settings[key]) is bool:
|
||||
tempsettings[key] = {
|
||||
"value" : boeol,
|
||||
"type" : type(settings[key])
|
||||
}
|
||||
else:
|
||||
tempsettings[key] = {
|
||||
"value" : value,
|
||||
"type" : type(settings[key])
|
||||
}
|
||||
|
||||
save = ttk.Button(frameunder, command=lambda: helpers.savefile(file, tempsettings), text="SAVE")
|
||||
save.grid(row=i, column=2, sticky=tk.E, pady=2)
|
||||
frameunder.grid()
|
||||
|
||||
|
||||
def servermenu(mainframe, configfile):
|
||||
for child in mainframe.winfo_children(): child.destroy() #oneline to destroy everything in the main frame
|
||||
side1 = ttk.Frame(mainframe)
|
||||
side2 = ttk.Frame(mainframe)
|
||||
# Side one of the 'server' menu
|
||||
logarea = ScrolledText(side2,
|
||||
wrap = tk.WORD,
|
||||
state="disabled")
|
||||
|
||||
startb = ttk.Button(side1, text="Start Server", command=lambda: [helpers.bgthread(helpers.serverbackground, [helpers.getsnpath(configfile), logarea])])
|
||||
startb.grid(row = 0, column = 0, sticky = tk.W, pady = 2)
|
||||
# Side two of the 'server' menu
|
||||
|
||||
logarea.grid(row = 0, column = 0, sticky = tk.E, pady = 2)
|
||||
|
||||
side1.grid(row = 0, column = 0, sticky = tk.N, pady = 2)
|
||||
side2.grid(row = 0, column = 1, sticky = tk.N, pady = 2)
|
||||
|
||||
class helpers:
|
||||
def downloadlatestdll(filepath):
|
||||
x = requests.get('https://openwf.io/supplementals/client%20drop-in/meta')
|
||||
meta = json.loads(x.text)
|
||||
url = f"https://openwf.io/supplementals/client%20drop-in/{meta['version']}/dwmapi.dll"
|
||||
|
||||
content = requests.get(url, stream=True).content
|
||||
hashofdll = sha256()
|
||||
hashofdll.update(content)
|
||||
|
||||
if hashofdll.hexdigest() == meta['sha256']:
|
||||
with open(filepath + "dwmapi.dll", "wb") as out_file:
|
||||
out_file.write(content)
|
||||
messagebox.showinfo(title="Bootstrapper Downloaded", message="Bootstrapper Downloaded!")
|
||||
else:
|
||||
messagebox.showerror(title="Unknown Error.", message="SpaceninGUI could not verify the hash of the Bootstrapper. Sorry :(")
|
||||
|
||||
def check4client(clientpath):
|
||||
onlyfiles = [f for f in listdir(clientpath) if isfile(join(clientpath, f))]
|
||||
if "dwmapi.dll" in onlyfiles:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Not implemented yet lol
|
||||
def runpluto(name):
|
||||
pass
|
||||
|
||||
def is_number(data):
|
||||
"""Validate the contents of an entry widget as a int."""
|
||||
if data == '':
|
||||
return True
|
||||
try:
|
||||
rv = int(data)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def savefile(file, save):
|
||||
if Path(file).is_file:
|
||||
with open(file) as f:
|
||||
data = json.load(f)
|
||||
|
||||
for key in save.keys():
|
||||
if save[key]["type"] is bool:
|
||||
if save[key]['value'].get() == '1':
|
||||
data[key] = True
|
||||
else:
|
||||
data[key] = False
|
||||
print(save[key]['value'].get())
|
||||
elif save[key]["type"] is str:
|
||||
data[key] = str(save[key]["value"].get())
|
||||
elif save[key]["type"] is int:
|
||||
data[key] = int(save[key]["value"].get())
|
||||
elif save[key]["type"] is dict:
|
||||
for k in save[key]['value'].keys():
|
||||
if save[key]['value'][k]["type"] is bool:
|
||||
data[key][k] = bool(save[key]['value'][k]["valueop"].get())
|
||||
elif save[key]['value'][k]["type"] is str:
|
||||
data[key][k] = str(save[key]['value'][k]["value"].get())
|
||||
elif save[key]['value'][k]["type"] is int:
|
||||
data[key][k] = int(save[key]['value'][k]["value"].get())
|
||||
|
||||
with open(file, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
def getclientpath(file):
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
d = json.load(f)
|
||||
return d["wfpath"]
|
||||
else:
|
||||
return False
|
||||
|
||||
# Grabs the serverpath, returns false if non-existant
|
||||
def getsnpath(file):
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
d = json.load(f)
|
||||
return d["spaceninjapath"]
|
||||
else:
|
||||
return False
|
||||
|
||||
def getlogmax():
|
||||
file = 'sngconfig.json'
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
d = json.load(f)
|
||||
return int(d["maxlogsize"])
|
||||
else:
|
||||
return False
|
||||
|
||||
def bgthread(func, args):
|
||||
th = threading.Thread(target=func, args=args)
|
||||
th.daemon = True
|
||||
th.start()
|
||||
|
||||
def serverbackground(cd, logarea): # To my knowledge, running the server in the main thread would stall the gui, fuck that lol
|
||||
logmax = helpers.getlogmax()
|
||||
|
||||
def mongo():
|
||||
sysctl = Popen(['systemctl', 'start', 'mongodb'])
|
||||
sysctl.wait()
|
||||
|
||||
|
||||
def execute(cd):
|
||||
popen = Popen(['npm', 'run', 'dev'], stdin=PIPE, stdout=PIPE, universal_newlines=True, cwd=cd)
|
||||
for stdout_line in iter(popen.stdout.readline, ""):
|
||||
yield stdout_line
|
||||
popen.stdout.close()
|
||||
return_code = popen.wait()
|
||||
if return_code:
|
||||
raise subprocess.CalledProcessError(return_code)
|
||||
|
||||
if platform == "linux":
|
||||
print("Running on linux requires mongodb to be started!")
|
||||
mongo()
|
||||
|
||||
for path in execute(cd):
|
||||
if len(logarr) < logmax:
|
||||
logarr.append(path)
|
||||
else:
|
||||
logarr.pop(0)
|
||||
logarr.append(path)
|
||||
if logarea:
|
||||
logarea.configure(state="normal")
|
||||
logarea.delete(1.0, "end")
|
||||
for element in logarr:
|
||||
logarea.insert(tk.INSERT, element)
|
||||
logarea.configure(state="disabled")
|
||||
|
||||
def check4sn(configfile):
|
||||
file = helpers.getsnpath(configfile) + "package.json"
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
d = json.load(f)
|
||||
if (d["name"] == "wf-emulator"):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def on_closing(root):
|
||||
if messagebox.askokcancel("Quit", "Do you want to quit?"):
|
||||
root.destroy()
|
||||
|
||||
class main:
|
||||
def __init__(self, configfile):
|
||||
|
||||
root = ThemedTk(theme='black', themebg=True)
|
||||
root.title('SpaceNinGui Version 1.0')
|
||||
root.minsize(400, 300)
|
||||
serverlogs = tk.StringVar()
|
||||
wfemucheck = str(helpers.check4sn(configfile))
|
||||
menubar = tk.Menu(root)
|
||||
vcmd = root.register(helpers.is_number)
|
||||
filemenu = tk.Menu(menubar, tearoff=0)
|
||||
filemenu.add_command(label="Options", command=lambda: menus.settingsmenu(file = configfile, mainframe=mainframe, vcmd=vcmd))
|
||||
filemenu.add_separator()
|
||||
filemenu.add_command(label="Exit", command=lambda: helpers.on_closing(root))
|
||||
menubar.add_cascade(label="SpaceNinGUI", menu=filemenu)
|
||||
menubar.add_separator()
|
||||
|
||||
menubar.add_command(label="\u22EE", activebackground=menubar.cget("background"))
|
||||
|
||||
menubar.add_command(label="Server", command=lambda: menus.servermenu(mainframe, configfile))
|
||||
menubar.add_command(label="Settings", command=lambda: menus.settingsmenu(file=(helpers.getsnpath(configfile) + "config.json"), mainframe=mainframe, vcmd=vcmd))
|
||||
mainframe = ttk.Frame(root)
|
||||
menubar.add_command(label="\u22EE", activebackground=menubar.cget("background"))
|
||||
menubar.add_command(label="Client", command=lambda: menus.clientmenu(file="sngconfig.json", mainframe=mainframe))
|
||||
menubar.add_command(label="Settings", command=lambda: menus.settingsmenu(file=(helpers.getclientpath(configfile) + "/OpenWF/client_config.json"), mainframe=mainframe, vcmd=vcmd))
|
||||
menubar.add_command(label="Scripts", command=lambda: menus.plutomenu(file="sngconfig.json", mainframe=mainframe))
|
||||
|
||||
|
||||
|
||||
|
||||
checklabel = ttk.Label(mainframe, text=f"Was Spaceninja Found: {wfemucheck}", anchor=tk.W, font=("Arial", 16, "bold"))
|
||||
checklabel.grid(row = 0, column = 0, sticky = tk.W, pady = 2)
|
||||
mainframe.grid(row = 0, column = 0, pady = 2)
|
||||
root.protocol("WM_DELETE_WINDOW", lambda: helpers.on_closing(root))
|
||||
root.config(menu=menubar)
|
||||
root.mainloop()
|
||||
|
||||
class install():
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.step = 1
|
||||
|
||||
self.config = {}
|
||||
self.elements = {}
|
||||
|
||||
root = ThemedTk(theme='black', themebg=True)
|
||||
self.root = root
|
||||
self.installstep=tk.StringVar()
|
||||
self.installprog=tk.IntVar()
|
||||
self.installprog.set(0)
|
||||
self.installstep.set("Init")
|
||||
#font options lmao
|
||||
standard = ('Noto Sans', 10)
|
||||
standardbold = ('Noto Sans', 10, "bold")
|
||||
self.standard = standard
|
||||
self.standardbold = standardbold
|
||||
|
||||
root.title('SpaceNinGui Installer')
|
||||
root.minsize(400, 300)
|
||||
leftframe = ttk.Frame(root, borderwidth=10)
|
||||
rightframe = ttk.Frame(root, borderwidth=1)
|
||||
steps=["1: Initial Message", "2: Install Spaceninja Server", "3: Install Warframe Bootstrapper", "4: Final Steps"]
|
||||
ttk.Label(leftframe, text="Steps:").grid(padx=20)
|
||||
self.steplabel=[]
|
||||
for step in steps:
|
||||
label=ttk.Label(leftframe, text=step, anchor=tk.W)
|
||||
self.steplabel.append(label)
|
||||
for label in self.steplabel:
|
||||
label.grid(padx=20, pady=5, sticky=tk.W)
|
||||
|
||||
|
||||
self.steplabel[0].config(font=standardbold)
|
||||
rightinnerframe = ttk.Frame(rightframe)
|
||||
ttk.Label(rightinnerframe, text="Welcome to SpaceNinGui! \nFirst, We have to get some inital settings setup. \nWe will start with the server configuration.").grid()
|
||||
leftframe.grid(row=0, column=0, sticky=tk.W)
|
||||
|
||||
rightinnerframe.grid()
|
||||
rightframe.grid(row=0, column=1, sticky=tk.E)
|
||||
nextbutton = ttk.Button(rightframe, text="Next", command=lambda: self.next(rightinnerframe))
|
||||
nextbutton.grid(row=1, sticky=tk.SE)
|
||||
root.protocol("WM_DELETE_WINDOW", lambda: helpers.on_closing(root))
|
||||
root.mainloop()
|
||||
|
||||
def configsave(self):
|
||||
with open('sngconfig.json', 'w') as f:
|
||||
tot = {}
|
||||
tot["spaceninjapath"] = self.elements['serverpath'].get()
|
||||
tot["wfpath"] = self.elements['clientpath'].get()
|
||||
tot['maxlogsize']=10
|
||||
json.dump(tot, f, indent=2)
|
||||
|
||||
|
||||
|
||||
def install(self, clientpath, serverpath):
|
||||
#first install server
|
||||
self.installstep.set("Downloading Server Through Git")
|
||||
sysctl = Popen(['git', 'clone', 'https://openwf.io/SpaceNinjaServer.git', serverpath])
|
||||
self.installprog.set(1)
|
||||
sysctl.wait()
|
||||
self.installstep.set("Installing Server Requirements (via npm)")
|
||||
self.installprog.set(25)
|
||||
sysctl = Popen(["npm", '--prefix', f'{serverpath}', 'install'])
|
||||
sysctl.wait()
|
||||
self.installstep.set("Copying Config File")
|
||||
shutil.copy(serverpath + "/config.json.example", serverpath + "/config.json")
|
||||
self.installstep.set("Starting Bootstrapper Download")
|
||||
self.installprog.set(50)
|
||||
print("Server install worked. ")
|
||||
helpers.downloadlatestdll(clientpath)
|
||||
self.installprog.set(99)
|
||||
self.installstep.set("Install Complete!\nPress next then restart to get started!")
|
||||
|
||||
|
||||
def next(self, inner):
|
||||
if self.step == 1:
|
||||
self.step+=1
|
||||
for child in inner.winfo_children(): child.destroy()
|
||||
self.elements["serverpath"] = tk.StringVar()
|
||||
path = ttk.Entry(inner, width=50, textvariable=self.elements["serverpath"])
|
||||
flavortext = ttk.Label(inner, text="Please input a path that you would like to install the Server in!")
|
||||
flavortext.grid(padx=10)
|
||||
path.grid(padx=10)
|
||||
self.steplabel[0].config(font=self.standard)
|
||||
self.steplabel[1].config(font=self.standardbold)
|
||||
elif self.step == 2:
|
||||
self.step+=1
|
||||
self.elements["clientpath"] = tk.StringVar()
|
||||
for child in inner.winfo_children(): child.destroy()
|
||||
path = ttk.Entry(inner, width=50, textvariable=self.elements["clientpath"])
|
||||
flavortext = ttk.Label(inner, text="Please input the path that has Warframe installed\nI highly suggest making a copy to prevent potental bans.", anchor=tk.CENTER)
|
||||
flavortext.grid(padx=10, pady=5)
|
||||
path.grid(padx=10)
|
||||
self.elements["wfpath"] = path
|
||||
self.steplabel[1].config(font=self.standard)
|
||||
self.steplabel[2].config(font=self.standardbold)
|
||||
elif self.step == 3:
|
||||
self.step+= 1
|
||||
print("final step!!!")
|
||||
for child in inner.winfo_children(): child.destroy()
|
||||
flavortext = ttk.Label(inner, text="Thank you! The OpenWF setup should be downloading in the background!", anchor=tk.CENTER)
|
||||
flavortext.grid(padx=10, pady=5)
|
||||
statustext = ttk.Label(inner, textvariable=self.installstep, anchor=tk.CENTER)
|
||||
statustext.grid(padx=10, pady=5)
|
||||
statusbar = ttk.Progressbar(inner, variable=self.installprog)
|
||||
statusbar.grid()
|
||||
helpers.bgthread(self.install, [self.elements["clientpath"].get(), self.elements["serverpath"].get()])
|
||||
elif self.step == 4:
|
||||
if self.installstep.get() == "Install Complete!\nPress next then restart to get started!":
|
||||
self.configsave()
|
||||
self.root.destroy()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if isfile('sngconfig.json'):
|
||||
main('sngconfig.json')
|
||||
else:
|
||||
install()
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
|
Loading…
x
Reference in New Issue
Block a user