Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
7f5cf9e8a4 | |||
d80a137ba6 | |||
bf1ea6b0f8 | |||
9da204da1d | |||
0970e28f0c | |||
957dd73e59 | |||
61acd551ea | |||
cc433a5bbf | |||
37f33f49ca | |||
ee9e2c1de3 | |||
87d0d92f4d |
@ -1,5 +1,7 @@
|
||||
# spaceningui
|
||||
|
||||
Simple Gui Program to help install SNS and the bootstrapper.
|
||||
Simple Gui Program to help install and manage SNS and the bootstrapper.
|
||||
|
||||
Make sure to have python3 installed, and to install all the requirents listed in the .txt. Mongo, git, and Node.js are also required.
|
||||
Make sure to have python3 installed, and to install all the requirents listed in the .txt *(When updated). Mongo, git, and Node.js are also required.
|
||||
|
||||
This branch is the majorly rewritten dev branch. I wasn't happy on how I managed the inital project, especially regarding the gui. The current version will hopefully fix much of that
|
893
__main__.py
893
__main__.py
@ -1,455 +1,548 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from tkinter import messagebox, font
|
||||
from ttkthemes import ThemedTk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
from os.path import isfile, join, exists
|
||||
import customtkinter
|
||||
from CTkMessagebox import CTkMessagebox
|
||||
from subfiles import helpers, install, savetools, themes, comunic
|
||||
|
||||
import json
|
||||
import webbrowser
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
from pathlib import Path
|
||||
import threading
|
||||
|
||||
from pathlib import Path
|
||||
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])
|
||||
from websockets.sync.client import connect
|
||||
|
||||
|
||||
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])
|
||||
}
|
||||
customtkinter.set_appearance_mode("System")
|
||||
customtkinter.set_window_scaling(1)
|
||||
customtkinter.set_widget_scaling(1)
|
||||
|
||||
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 placeholder():
|
||||
print("placeholder called")
|
||||
pass
|
||||
|
||||
class menuTools:
|
||||
def selectFromList(self, itemList, iconList, textvar):
|
||||
#there is a LOT here, I really should make a "menus" file lol
|
||||
self.truePath=''
|
||||
print(textvar.get())
|
||||
self.threadRunning=False
|
||||
currentOption= customtkinter.StringVar()
|
||||
currentOption.set("Current Option: None")
|
||||
|
||||
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")
|
||||
self.currentImage = helpers.getIcon(self, "NA", "/placeholder.png")
|
||||
|
||||
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
|
||||
currentLimit = customtkinter.StringVar()
|
||||
|
||||
logarea.grid(row = 0, column = 0, sticky = tk.E, pady = 2)
|
||||
newWindow = customtkinter.CTkToplevel(self.root)
|
||||
newWindow.title("Select An Option")
|
||||
newWindow.geometry("600x400")
|
||||
|
||||
side1.grid(row = 0, column = 0, sticky = tk.N, pady = 2)
|
||||
side2.grid(row = 0, column = 1, sticky = tk.N, pady = 2)
|
||||
customtkinter.CTkLabel(newWindow, text="Please Choose an Option", anchor='center').grid(row=0, column=0, sticky="we")
|
||||
|
||||
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"
|
||||
centerFrame = customtkinter.CTkFrame(newWindow, width=600, height=200, fg_color="transparent")
|
||||
centerFrame.pack_propagate(False)
|
||||
|
||||
content = requests.get(url, stream=True).content
|
||||
hashofdll = sha256()
|
||||
hashofdll.update(content)
|
||||
listFrame= customtkinter.CTkScrollableFrame(centerFrame, height=200, width=300, bg_color="transparent")
|
||||
self.listFrameInner = customtkinter.CTkFrame(listFrame)
|
||||
self.listFrameInner.pack()
|
||||
def submit(self, truePath, textvar, window):
|
||||
|
||||
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)
|
||||
print(f"truePath : {self.truePath}")
|
||||
print(textvar.get())
|
||||
if currentOption.get() == '':
|
||||
window.destroy()
|
||||
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")
|
||||
textvar.set(self.truePath)
|
||||
window.destroy()
|
||||
|
||||
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()
|
||||
def change(self, path, name, textVar, iconList):
|
||||
textVar.set(f"Current Option: {name}")
|
||||
print(path)
|
||||
self.truePath=path
|
||||
print(self.truePath)
|
||||
self.currentImage=helpers.getIcon(self, path, iconList[path])
|
||||
icon=customtkinter.CTkImage(dark_image=self.currentImage, size=(200, 200))
|
||||
self.iconLabel.destroy()
|
||||
self.iconLabel = customtkinter.CTkLabel(sideFrame, text="", image=icon, width=275, height=200)
|
||||
self.iconLabel.grid(row=0, column=0)
|
||||
|
||||
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"))
|
||||
def outsideThread(self, listFrame, itemList, limit, truePath):
|
||||
i = 0
|
||||
for element in itemList:
|
||||
if limit in element["name"].lower(): ass= True
|
||||
else: ass= False
|
||||
print(f"{element["name"]} : {str(ass)}")
|
||||
if limit in element["name"].lower():
|
||||
|
||||
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))
|
||||
if "slot" in element:
|
||||
button = customtkinter.CTkButton(self.listFrameInner, text=(f"{element["name"]} - SLOT {str(element["slot"])}"), border_color="black", anchor="w", fg_color="transparent", width = 299, command=lambda element=element: change(self, element["uniqueName"], element["name"], currentOption, iconList))
|
||||
else:
|
||||
i+=1
|
||||
button = customtkinter.CTkButton(self.listFrameInner, text=(f"{element["name"]} - NO SLOT"), border_color="black", anchor="w", fg_color="transparent", width = 299, command=lambda element=element: change(self, element["uniqueName"], element["name"], currentOption, iconList))
|
||||
button.pack(anchor="nw", expand=True)
|
||||
print(i)
|
||||
|
||||
self.threadRunning=False
|
||||
|
||||
def reset(self, args):
|
||||
if self.threadRunning:
|
||||
return #If the search thread is running, just wait, don't be fucking lazy, it takes 2 seconds for THE ENTIRE LIST, it is not that hard
|
||||
helpers.removeChildren(self.listFrameInner)
|
||||
helpers.bgThread(outsideThread, args)
|
||||
|
||||
helpers.bgThread(outsideThread, [self, listFrame, itemList, '', self.truePath])
|
||||
|
||||
sideFrame= customtkinter.CTkFrame(centerFrame, height=200, width=275, fg_color="transparent")
|
||||
|
||||
icon=customtkinter.CTkImage(dark_image=self.currentImage, size=(200, 200))
|
||||
self.iconLabel = customtkinter.CTkLabel(sideFrame, text="", image=icon, width=275, height=200)
|
||||
self.iconLabel.grid(row=0, column=0)
|
||||
sideFrame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
|
||||
searchFrame=customtkinter.CTkFrame(newWindow, width=600)
|
||||
searchFrame.grid(row=2, column=0)
|
||||
search=customtkinter.CTkEntry(searchFrame, textvariable=currentLimit, fg_color="transparent", width=400)
|
||||
search.grid(row=0, column=0)
|
||||
|
||||
searchButton=customtkinter.CTkButton(searchFrame, width=200, text="Search", command=lambda: reset(self, [self, listFrame, itemList, currentLimit.get(), self.truePath]))
|
||||
searchButton.grid(row=0, column=1)
|
||||
|
||||
optionLabel= customtkinter.CTkLabel(newWindow, textvariable=currentOption)
|
||||
|
||||
submitButton=customtkinter.CTkButton(newWindow, width=100, text="Submit", height=30, command= lambda: submit(self,self.truePath,textvar,newWindow))
|
||||
submitButton.grid(row=4, column=0)
|
||||
|
||||
|
||||
centerFrame.grid(row=1, column=0)
|
||||
listFrame.grid(row=0, column=1)
|
||||
|
||||
optionLabel.grid(row=3, column=0)
|
||||
|
||||
def verticalButtons(self, buttonList, frame):
|
||||
for button in buttonList.keys():
|
||||
sideButton = customtkinter.CTkButton(frame, height=28, hover_color=self.theme["highlight_color"],
|
||||
border_color=self.theme["highlight_color"],
|
||||
border_width=(self.x * .001),
|
||||
fg_color="transparent",
|
||||
bg_color="transparent",
|
||||
text=button,
|
||||
command=buttonList[button])
|
||||
sideButton.pack(pady=(self.y * .01), padx=2, side="top", anchor="nw")
|
||||
|
||||
def labelDown(self, labelList, frame, column):
|
||||
newFrame=customtkinter.CTkFrame(frame, fg_color="transparent")
|
||||
newFrame.grid(row=0, column=3, sticky="nswe")
|
||||
|
||||
i=1
|
||||
l = []
|
||||
for labelText in labelList:
|
||||
label = customtkinter.CTkLabel(newFrame, textvariable=labelText, height=28, text_color="white")
|
||||
l.append(label)
|
||||
|
||||
for g in l:
|
||||
g.pack(anchor='nw')
|
||||
i+=1
|
||||
|
||||
def horizontalButtons(self, buttonList, frame):
|
||||
for button in buttonList.keys():
|
||||
sideButton = customtkinter.CTkButton(frame, height=28, hover_color=self.theme["highlight_color"],
|
||||
border_color=self.theme["highlight_color"],
|
||||
border_width=(self.x * .001),
|
||||
fg_color="transparent",
|
||||
bg_color="transparent",
|
||||
text=button,
|
||||
command=buttonList[button])
|
||||
sideButton.pack(pady=(self.x * .01), padx=2, side="left", anchor="w")
|
||||
|
||||
|
||||
|
||||
|
||||
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 menus():
|
||||
|
||||
class install():
|
||||
def serverMenu(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.step = 1
|
||||
sideFrame = customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .25), height=self.y*.8, fg_color=self.theme['bg_deep'])
|
||||
sideFrame.grid(row=0, column=0, sticky="ns")
|
||||
|
||||
self.config = {}
|
||||
self.elements = {}
|
||||
borderFrame=customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .01), height=(self.y*.8), fg_color=self.theme['bg_color'], corner_radius=0)
|
||||
borderFrame.grid(row=0, column=1, sticky="ns")
|
||||
sideFrame.grid_propagate(1)
|
||||
|
||||
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
|
||||
buttonList = {
|
||||
"Start Server" : lambda: helpers.bgThread(comunic.startServer, [self, self.serverPath, self.logArea]),
|
||||
"Open Server Location": lambda: helpers.openLocation(self, comunic.getSnPath('sngconfig.json'))
|
||||
}
|
||||
menuTools.verticalButtons(self, buttonList=buttonList, frame=sideFrame)
|
||||
|
||||
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.logArea = customtkinter.CTkTextbox(topFrame, width=(self.x * .7 * .74))
|
||||
self.logArea.grid(row=0, column=2, sticky="ns")
|
||||
|
||||
def updateMenu(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
|
||||
|
||||
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)
|
||||
sideFrame = customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .25), height=self.y*.8, fg_color="transparent", corner_radius=10)
|
||||
sideFrame.grid(row=0, column=0, sticky="ns")
|
||||
|
||||
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()
|
||||
borderFrame=customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .01), height=(self.y*.8), fg_color=self.theme['bg_color'], corner_radius=0)
|
||||
borderFrame.grid(row=0, column=1, sticky="ns")
|
||||
sideFrame.grid_propagate(1)
|
||||
|
||||
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)
|
||||
self.versions = [
|
||||
customtkinter.StringVar(),
|
||||
customtkinter.StringVar()
|
||||
]
|
||||
|
||||
buttonList = {
|
||||
"Open Client Location" : lambda: helpers.openLocation(self, self.clientPath),
|
||||
"Open Server Location": lambda: helpers.openLocation(self, comunic.getSnPath(self.configFile)),
|
||||
"Update Client Bootstrapper" : lambda: comunic.downloadLatestDll(self, self.clientPath),
|
||||
"Update Server" : lambda: placeholder()
|
||||
}
|
||||
menuTools.verticalButtons(self, buttonList=buttonList, frame=sideFrame)
|
||||
|
||||
|
||||
self.versions[0].set("Checking Server Version")
|
||||
self.versions[1].set("Checking Client Version")
|
||||
|
||||
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!")
|
||||
helpers.bgThread(comunic.getLatestVersions, [self, 'tempFile.json', self.versions])
|
||||
|
||||
|
||||
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()
|
||||
menuTools.labelDown(self, labelList=self.versions, frame=topFrame, column=3)
|
||||
|
||||
def clientMenu(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
|
||||
menuBar = customtkinter.CTkFrame(topFrame, fg_color=self.theme["bg_deep"], width=(self.x * (.70)), height=self.y * .1)
|
||||
menuBar.grid(row=0, column=0, sticky="we")
|
||||
|
||||
buttonList = {
|
||||
"Start Client (steam)" : lambda: helpers.bgThread(comunic.startClient, [self, json.load(open(self.configFile))["steamID"]]),
|
||||
"Open Logs Location" : lambda: comunic.openClientLogs(self, platform)
|
||||
}
|
||||
menuTools.horizontalButtons(self, buttonList, menuBar)
|
||||
|
||||
def settingsMenu(self, file):
|
||||
data = savetools.loadFile(self, file)
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
topFrame = customtkinter.CTkScrollableFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
newData = {}
|
||||
def recusiveIssue(self, data, newData, topFrame, recLayer, i):
|
||||
print("Length : " + str(i) + " Type : " + str(type(data)))
|
||||
if type(data) is dict:
|
||||
|
||||
for key in data.keys():
|
||||
layerArr = ""
|
||||
for n in range(recLayer):
|
||||
layerArr = layerArr + " "
|
||||
if recLayer > 0:
|
||||
layerArr=layerArr+"↳"
|
||||
l = customtkinter.CTkLabel(topFrame, text=layerArr + key, anchor='w')
|
||||
l.grid(row=i, column=0, sticky='w')
|
||||
|
||||
|
||||
print(key + f" : {str(i)}")
|
||||
|
||||
match data[key]:
|
||||
case str():
|
||||
newData[key] = customtkinter.StringVar()
|
||||
newData[key].set(data[key])
|
||||
|
||||
e = customtkinter.CTkEntry(topFrame, textvariable=newData[key], width=self.x*.5)
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case bool():
|
||||
newData[key] = customtkinter.BooleanVar()
|
||||
newData[key].set(data[key])
|
||||
|
||||
e = customtkinter.CTkSwitch(topFrame, variable=newData[key], onvalue=True, offvalue=False, text='')
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case int():
|
||||
newData[key] = customtkinter.StringVar()
|
||||
newData[key].set(str(data[key]))
|
||||
|
||||
e = customtkinter.CTkEntry(topFrame, textvariable=newData[key], width=self.x*.5, validate='key', validatecommand=(self.verifyNumb, '%P'))
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case dict():
|
||||
i+=1
|
||||
newData[key] = {}
|
||||
i = recusiveIssue(self, data[key], newData[key], topFrame, (recLayer + 1), i)
|
||||
case list():
|
||||
i+=1
|
||||
newData[key] = []
|
||||
i = recusiveIssue(self, data[key], newData[key], topFrame, (recLayer + 1), i)
|
||||
|
||||
|
||||
elif type(data) is list:
|
||||
for key in range(len(data)):
|
||||
layerArr = ""
|
||||
for n in range(recLayer):
|
||||
layerArr = layerArr + " "
|
||||
if recLayer > 0:
|
||||
layerArr=layerArr+"↳"
|
||||
l = customtkinter.CTkLabel(topFrame, text=layerArr + str(key), anchor='w')
|
||||
l.grid(row=i, column=0, sticky='w')
|
||||
|
||||
print(str(key) + " key")
|
||||
|
||||
|
||||
match data[key]:
|
||||
|
||||
case str():
|
||||
newData.append(customtkinter.StringVar())
|
||||
newData[key].set(data[key])
|
||||
|
||||
e = customtkinter.CTkEntry(topFrame, textvariable=newData[key], width=self.x*.5)
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case bool():
|
||||
newData.append(customtkinter.BooleanVar())
|
||||
newData[key].set(data[key])
|
||||
|
||||
e = customtkinter.CTkSwitch(topFrame, variable=newData[key], onvalue=True, offvalue=False, text='')
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case int():
|
||||
newData.append(customtkinter.IntVar())
|
||||
newData[key].set(data[key])
|
||||
|
||||
e = customtkinter.CTkEntry(topFrame, textvariable=newData[key], width=self.x*.5)
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
case dict():
|
||||
i+=1
|
||||
newData.append({})
|
||||
i = recusiveIssue(self, data[key], newData[key], topFrame, (recLayer + 1), i)
|
||||
case list():
|
||||
i+=1
|
||||
newData.append([])
|
||||
i = recusiveIssue(self, data[key], newData[key], topFrame, (recLayer + 1), i)
|
||||
return i
|
||||
i=0
|
||||
i=recusiveIssue(self, data, newData, topFrame, 0, i)
|
||||
saveButton = customtkinter.CTkButton(topFrame, height=28, hover_color=self.theme["highlight_color"],
|
||||
border_color=self.theme["highlight_color"],
|
||||
border_width=(self.x * .001),
|
||||
fg_color="transparent",
|
||||
bg_color="transparent",
|
||||
text="Save Settings",
|
||||
command=lambda: savetools.saveFile(self, newData, file))
|
||||
saveButton.grid(row=i, column=0, sticky='w')
|
||||
|
||||
def plutoMenu(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
plutoList=[f for f in listdir(self.clientPath+'/OpenWF/scripts/') if isfile(join(self.clientPath + "/OpenWF/scripts/", f))]
|
||||
runningList={}
|
||||
print(plutoList)
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
topFrame.columnconfigure(1, weight=1)
|
||||
i=0
|
||||
for pluto in plutoList:
|
||||
runningList[pluto] = customtkinter.BooleanVar()
|
||||
label = customtkinter.CTkLabel(topFrame, text=pluto)
|
||||
label.grid(row=i, column=0, sticky="nsw", padx=10)
|
||||
toggle = customtkinter
|
||||
e = customtkinter.CTkSwitch(topFrame, variable=runningList[pluto], command=lambda pluto=pluto: comunic.runPluto(self, pluto, runningList), text='')
|
||||
e.grid(row=i, column=1, padx=10)
|
||||
i+=1
|
||||
print(pluto)
|
||||
|
||||
def inlineScripting(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),
|
||||
)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
|
||||
sideFrame = customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .25), height=self.y*.8, fg_color=self.theme['bg_deep'])
|
||||
sideFrame.grid(row=0, column=0, sticky="ns")
|
||||
|
||||
borderFrame=customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .01), height=(self.y*.8), fg_color=self.theme['bg_color'], corner_radius=0)
|
||||
borderFrame.grid(row=0, column=1, sticky="ns")
|
||||
sideFrame.grid_propagate(1)
|
||||
|
||||
buttonList = {
|
||||
"Push Inline" : lambda: comunic.pushInline(self, self.logArea)
|
||||
}
|
||||
menuTools.verticalButtons(self, buttonList=buttonList, frame=sideFrame)
|
||||
|
||||
self.logArea = customtkinter.CTkTextbox(topFrame, width=(self.x * .7 * .74))
|
||||
self.logArea.grid(row=0, column=2, sticky="ns")
|
||||
|
||||
def arsenalMenu(self):
|
||||
helpers.removeChildren(self.mainFrame)
|
||||
#Load weapons from cache
|
||||
try:
|
||||
self.weapons=helpers.formatExportWeapons(self, './cache/items/weapons.json')
|
||||
self.powersuits=helpers.formatExportPowersuits(self, './cache/items/powersuits.json')
|
||||
self.weaponsIcons=helpers.formatExportPlus(self, './cache/items/plus/weapons.json')
|
||||
self.powersuitsIcons=helpers.formatExportPlus(self, './cache/items/plus/powersuits.json')
|
||||
except:
|
||||
print("FUCK")
|
||||
comunic.getWarframeExports(self, "./cache/items/")
|
||||
comunic.getWarframeExportsPlus(self, "./cache/items/")
|
||||
self.weapons=helpers.formatExportWeapons(self, './cache/items/weapons.json')
|
||||
self.powersuits=helpers.formatExportPowersuits(self, './cache/items/powersuits.json')
|
||||
self.weaponsIcons=helpers.formatExportPlus(self, './cache/items/plus/weapons.json')
|
||||
self.powersuitsIcons=helpers.formatExportPlus(self, './cache/items/plus/powersuits.json')
|
||||
|
||||
topFrame = customtkinter.CTkFrame(self.mainFrame, fg_color=self.theme["bg_deep"], corner_radius=10, height=(self.y * .80), width=(self.x * (.70)),)
|
||||
topFrame.pack(pady=(self.y * .10))
|
||||
topFrame.grid_propagate(0)
|
||||
|
||||
leftFrame = customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .25), height=self.y*.8, fg_color=self.theme['bg_deep'])
|
||||
leftFrame.grid(row=0, column=0, sticky="ns")
|
||||
|
||||
borderFrame=customtkinter.CTkFrame(topFrame, width=(self.x * .7 * .01), height=(self.y*.8), fg_color=self.theme['bg_color'], corner_radius=0)
|
||||
borderFrame.grid(row=0, column=1, sticky="ns")
|
||||
leftFrame.grid_propagate(1)
|
||||
|
||||
rightFrame = customtkinter.CTkFrame(topFrame, fg_color=self.theme['bg_deep'])
|
||||
rightFrame.grid(row=0, column=2, sticky="nsew")
|
||||
|
||||
selectedWeapons = {}
|
||||
i=0
|
||||
for entry in ["Warframe", "Primary", "Secondary", "Melee"]:
|
||||
selectedWeapons[entry] = customtkinter.StringVar()
|
||||
l=customtkinter.CTkLabel(rightFrame, text=entry)
|
||||
l.grid(row=i, column=0, padx=6)
|
||||
|
||||
e=customtkinter.CTkEntry(rightFrame, textvariable=selectedWeapons[entry], width=self.x*.3, fg_color=self.theme["fg_color"])
|
||||
e.grid(row=i, column=1, sticky="n", padx=10, pady=3)
|
||||
|
||||
if not (entry == "Warframe"):
|
||||
button=customtkinter.CTkButton(rightFrame, text="📁", width=5, command=lambda selectedWeapons=selectedWeapons[entry]: menuTools.selectFromList(self, self.weapons, self.weaponsIcons, selectedWeapons))
|
||||
elif entry == "Warframe":
|
||||
button=customtkinter.CTkButton(rightFrame, text="📁", width=5, command=lambda selectedWeapons=selectedWeapons[entry]: menuTools.selectFromList(self, self.powersuits, self.powersuitsIcons, selectedWeapons))
|
||||
#if not entry:
|
||||
# how?????
|
||||
button.grid(row=i, column=2)
|
||||
|
||||
|
||||
i+=1
|
||||
|
||||
buttonList = {
|
||||
"Push All" : lambda: comunic.pushWeapon(self, [selectedWeapons["Warframe"].get(),selectedWeapons["Primary"].get(),selectedWeapons["Secondary"].get(),selectedWeapons["Melee"].get()], [3, 1, 0, 5]),
|
||||
"Push Warframe" : lambda: comunic.pushWeapon(self, selectedWeapons["Warframe"].get(), 3),
|
||||
"Push Primary" : lambda: comunic.pushWeapon(self, selectedWeapons["Primary"].get(), 1),
|
||||
"Push Secondary" : lambda: comunic.pushWeapon(self, selectedWeapons["Secondary"].get(), 0),
|
||||
"Push Melee" : lambda: comunic.pushWeapon(self, selectedWeapons["Melee"].get(), 5)
|
||||
}
|
||||
|
||||
menuTools.verticalButtons(self, buttonList, leftFrame)
|
||||
|
||||
|
||||
class main():
|
||||
def exit(self):
|
||||
self.threadStop = True
|
||||
for thread in self.popenThreads:
|
||||
helpers.killPopen(thread)
|
||||
self.root.destroy()
|
||||
|
||||
def __init__(self, configFile):
|
||||
self.configFile = configFile
|
||||
self.clientPath = json.load(open(configFile))['wfpath']
|
||||
self.serverPath = json.load(open(configFile))['spaceninjapath']
|
||||
self.debug = False
|
||||
self.root = customtkinter.CTk()
|
||||
self.popenThreads = []
|
||||
self.x=750
|
||||
self.y=500
|
||||
self.theme = themes.getThemes(self)[json.load(open(configFile))['theme']]
|
||||
self.threadStop = False
|
||||
print()
|
||||
for lib in [helpers, install, savetools]:
|
||||
lib.check()
|
||||
self.rightFrame = customtkinter.CTkScrollableFrame(self.root,
|
||||
orientation="vertical",
|
||||
width=(self.x * .20),
|
||||
height=(self.y * 1),
|
||||
fg_color=self.theme["fg_color"],
|
||||
bg_color=self.theme["bg_color"],
|
||||
corner_radius=0
|
||||
)
|
||||
|
||||
|
||||
buttonList = {
|
||||
"Server" : lambda: menus.serverMenu(self),
|
||||
"Server Settings": lambda: menus.settingsMenu(self, self.serverPath+'config.json'),
|
||||
"Client" : lambda: menus.clientMenu(self),
|
||||
"Client Settings": lambda: menus.settingsMenu(self, self.clientPath+'OpenWF/client_config.json'),
|
||||
"Scripts" : lambda: menus.plutoMenu(self),
|
||||
"Script Store": lambda: placeholder(),
|
||||
"Inline Scripting" : lambda: menus.inlineScripting(self),
|
||||
"Metadata" : lambda: placeholder(),
|
||||
"Arsenal Management": lambda: menus.arsenalMenu(self),
|
||||
"Weapon Upgrades" : lambda: placeholder(),
|
||||
"Updates": lambda: menus.updateMenu(self),
|
||||
"GUI Settings" : lambda: menus.settingsMenu(self, self.configFile)
|
||||
}
|
||||
|
||||
menuTools.verticalButtons(self, buttonList, self.rightFrame)
|
||||
self.rightFrame.grid(row=0, column=0)
|
||||
|
||||
self.mainFrame = customtkinter.CTkScrollableFrame(self.root,
|
||||
width=(self.x * (1-.20)),
|
||||
height=(self.y * 1),
|
||||
fg_color=self.theme["bg_color"],
|
||||
bg_color=self.theme["bg_color"],
|
||||
corner_radius=0)
|
||||
self.verifyNumb = self.root.register(helpers.isNumber)
|
||||
|
||||
self.mainFrame.grid(row=0, column=1)
|
||||
|
||||
self.root.title("SpaceNinGui CustomTK 1.0.0")
|
||||
self.root.protocol("WM_DELETE_WINDOW", lambda: main.exit(self))
|
||||
self.root.geometry(f"{str(self.x)}x{str(self.y)}")
|
||||
self.root.resizable(width=False, height=False)
|
||||
self.root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if isfile('sngconfig.json'):
|
||||
main('sngconfig.json')
|
||||
else:
|
||||
install()
|
||||
pass
|
||||
|
BIN
cache/items/icons/placeholder.png
vendored
Normal file
BIN
cache/items/icons/placeholder.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
@ -1,2 +0,0 @@
|
||||
Requests==2.32.3
|
||||
ttkthemes==3.2.2
|
206
subfiles/comunic.py
Normal file
206
subfiles/comunic.py
Normal file
@ -0,0 +1,206 @@
|
||||
#communication stuff here
|
||||
|
||||
import requests
|
||||
import json
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
from shutil import copyfileobj
|
||||
from . import helpers, savetools
|
||||
from websockets.sync.client import connect
|
||||
from subprocess import Popen, PIPE
|
||||
from sys import platform
|
||||
from os import listdir
|
||||
from os.path import isfile, join, exists
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
def startClient(self, id):
|
||||
cmd = f"steam://rungameid/{id}"
|
||||
Popen(['steam', cmd])
|
||||
|
||||
def downloadLatestDll(self, 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']:
|
||||
print(filepath)
|
||||
with open(filepath + "dwmapi.dll", "wb") as out_file:
|
||||
out_file.write(content)
|
||||
helpers.messageBox(self=self,title="Bootstrapper Downloaded", message="Bootstrapper Downloaded!", type="info")
|
||||
else:
|
||||
helpers.messageBox(self=self,title="Unknown Error.", message="SpaceninGUI could not verify the hash of the Bootstrapper. Sorry :(", type="error")#
|
||||
|
||||
def getLatestVersions(self, cacheFile, value):
|
||||
ver = {
|
||||
"server" : (
|
||||
"no lol" #not finished update check for server, check back later :3
|
||||
),
|
||||
"client" : ""
|
||||
}
|
||||
#client code here
|
||||
x = requests.get('https://openwf.io/supplementals/client%20drop-in/meta')
|
||||
meta = json.loads(x.text)
|
||||
ver["client"] = meta['version']
|
||||
|
||||
try:
|
||||
oldVer = savetools.loadFile(self, cacheFile)["client"]
|
||||
except:
|
||||
with open('tempFile.json', 'w') as f:
|
||||
json.dump({
|
||||
"client":'notassigned',
|
||||
'server':'notassigned'
|
||||
},f,indent=2)
|
||||
|
||||
oldVer = savetools.loadFile(self, cacheFile)["client"]
|
||||
if oldVer == ver["client"]:
|
||||
client="Up To Date!"
|
||||
else:
|
||||
client="New Version " + ver["client"]
|
||||
server = "server Check Not Implemented"
|
||||
value[0].set("Latest Server Version: " + server)
|
||||
value[1].set("Latest Client Version: " + client)
|
||||
|
||||
def startServer(self, cd, logArea): # To my knowledge, running the server in the main thread would stall the gui, fuck that lol
|
||||
logmax = helpers.getLogMax(self, self.configFile)
|
||||
logarr=[]
|
||||
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, preexec_fn=os.setsid)
|
||||
self.popenThreads.append(popen)
|
||||
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)#
|
||||
def start(cd, logArea):
|
||||
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("end", element)
|
||||
logArea.configure(state="disabled")
|
||||
|
||||
if platform == "linux":
|
||||
if not self.popenThreads:
|
||||
helpers.messageBox(self, title="MongoDB", message="The mongodb server is required to start SNS\nYou will need to give sudo permission for this.", type="info")
|
||||
mongo()
|
||||
start(cd, logArea)
|
||||
else:
|
||||
helpers.messageBox(self, title="Already Running!", message="The Server is already running!", type="info")
|
||||
|
||||
def getClientResponse():
|
||||
try:
|
||||
with connect("ws://localhost:6155") as websocket:
|
||||
message = json.loads(websocket.recv())
|
||||
except:
|
||||
message=False
|
||||
return message
|
||||
|
||||
def checkForClient(clientpath):
|
||||
onlyfiles = [f for f in listdir(clientpath) if isfile(join(clientpath, f))]
|
||||
if "dwmapi.dll" in onlyfiles:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def runPluto(self, name, runningList):
|
||||
openwfpath = self.clientPath + "/OpenWF/scripts/"
|
||||
onlyfiles = [f for f in listdir(openwfpath) if isfile(join(openwfpath, f))]
|
||||
print("called " + name)
|
||||
if getClientResponse():
|
||||
running = []
|
||||
print(getClientResponse()["running_scripts"])
|
||||
if name in running:
|
||||
requests.get(url=("http://localhost:6155/stop_script?OpenWF/scripts/" + name))
|
||||
else:
|
||||
requests.get(url=("http://localhost:6155/start_script?OpenWF/scripts/" + name))
|
||||
|
||||
for script in getClientesponse()["running_scripts"]:
|
||||
running.append(script.removeprefix("OpenWF/scripts/"))#
|
||||
for file in onlyfiles:
|
||||
if file in running:
|
||||
if runningList[file]: runningList[file].set(True)
|
||||
else:
|
||||
if runningList[file]: runningList[file].set(False)
|
||||
else:
|
||||
if runningList[name]: runningList[name].set(False)
|
||||
|
||||
def getSnPath(file):
|
||||
if Path(file).is_file():
|
||||
with open(file) as f:
|
||||
d = json.load(f)
|
||||
return d["spaceninjapath"]
|
||||
else:
|
||||
return False
|
||||
|
||||
def openClientLogs(self, platform):
|
||||
if platform=="linux":
|
||||
print("ok")
|
||||
helpers.messageBox(self, "")
|
||||
|
||||
def pushWeapon(self, string, slot):
|
||||
if type(string) is list:
|
||||
i=0
|
||||
for part in string:
|
||||
print(i)
|
||||
pluto = f"gRegion:GetLocalPlayerAvatar():InventoryControl():RemoveItem({slot[i]}, true) gRegion:GetLocalPlayerAvatar():GiveItem(Type(\"{part}\"), true)"
|
||||
requests.get(f"http://localhost:61558/start_script_inline?{pluto}")
|
||||
if slot[i] == 3:
|
||||
level = f"gRegion:GetLocalPlayerAvatar():InventoryControl():GetActivePowerSuit():SetXP(1600000)"
|
||||
else:
|
||||
level = f"gRegion:GetLocalPlayerAvatar():InventoryControl():GetWeaponInHand({slot[i]}):SetXP(1600000)"
|
||||
requests.get(f"http://localhost:61558/start_script_inline?{level}")
|
||||
i+=1
|
||||
else:
|
||||
pluto = f"gRegion:GetLocalPlayerAvatar():InventoryControl():RemoveItem({slot}, true) gRegion:GetLocalPlayerAvatar():GiveItem(Type(\"{string}\"), true) gRegion:GetLocalPlayerAvatar():InventoryControl():GetWeaponInHand({slot}):SetXP(1600000)"
|
||||
requests.get(f"http://localhost:61558/start_script_inline?{pluto}")
|
||||
|
||||
def pushInline(self, Area):
|
||||
#format
|
||||
formatted = Area.get(0.0, "end").replace("\n", ' ')
|
||||
requests.get(f"http://localhost:61558/start_script_inline?{formatted}")
|
||||
|
||||
def getWarframeExports(self, path):
|
||||
exportList = {
|
||||
"weapons":"https://raw.githubusercontent.com/calamity-inc/warframe-public-export/refs/heads/senpai/ExportWeapons_en.json",
|
||||
"powersuits":"https://raw.githubusercontent.com/calamity-inc/warframe-public-export/refs/heads/senpai/ExportWarframes_en.json"
|
||||
}
|
||||
for export in exportList.keys():
|
||||
helpers.folderExist(path)
|
||||
with open(path + f"{export}.json", "wb") as out_file:
|
||||
content = requests.get(exportList[export], stream=True).content
|
||||
out_file.write(content)
|
||||
|
||||
def getWarframeExportsPlus(self, path):
|
||||
exportList = {
|
||||
"weapons":"https://raw.githubusercontent.com/calamity-inc/warframe-public-export-plus/refs/heads/senpai/ExportWeapons.json",
|
||||
"powersuits":"https://raw.githubusercontent.com/calamity-inc/warframe-public-export-plus/refs/heads/senpai/ExportWarframes.json"
|
||||
}
|
||||
for export in exportList.keys():
|
||||
helpers.folderExist((path+"/plus/").replace("//", "/"))
|
||||
with open((path + f"/plus/{export}.json").replace('//', '/'), "wb") as out_file:
|
||||
content = requests.get(exportList[export], stream=True).content
|
||||
out_file.write(content)
|
||||
|
||||
def getIconDownload(self, path, savePath):
|
||||
if not exists("./cache/items/icons" + savePath.rsplit('/', 1)[0]):
|
||||
Path("./cache/items/icons" + savePath.rsplit('/', 1)[0]).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open("./cache/items/icons" + savePath, "wb+") as out_file:
|
||||
print(f"https://browse.wf{savePath}")
|
||||
out_file.write(requests.get(f"https://browse.wf{savePath}").content)
|
||||
|
||||
print(f"Saved {path} Icon")
|
160
subfiles/helpers.py
Normal file
160
subfiles/helpers.py
Normal file
@ -0,0 +1,160 @@
|
||||
from CTkMessagebox import CTkMessagebox
|
||||
import threading
|
||||
from .savetools import loadFile
|
||||
from pathlib import Path
|
||||
from .comunic import getIconDownload
|
||||
import os
|
||||
import signal
|
||||
import json
|
||||
from PIL import Image
|
||||
|
||||
|
||||
try:
|
||||
from os import startfile
|
||||
except:
|
||||
from os import system
|
||||
from sys import platform
|
||||
def killPopen(popen): #jesus
|
||||
os.kill(os.getpgid(popen.pid), signal.SIGTERM)
|
||||
|
||||
def folderExist(path):
|
||||
Path(path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def check():
|
||||
print("Save Good")
|
||||
|
||||
def removeChildren(frame):
|
||||
for element in frame.winfo_children():
|
||||
element.destroy()
|
||||
|
||||
def bgThread(func, args):
|
||||
th = threading.Thread(target=func, args=args)
|
||||
th.start()
|
||||
|
||||
def openLocation(self, location):
|
||||
if platform == "linux":
|
||||
system('xdg-open "%s"' % location)
|
||||
elif platform == "windows":
|
||||
startfile(foldername)
|
||||
|
||||
def messageBox(self, title, message, type): #type will be implemented at a later point when required. for now, just fill it in irregardless.
|
||||
CTkMessagebox(title=title, message=message)
|
||||
|
||||
def getLogMax(self, save):
|
||||
return loadFile(self, save)['maxlogsize'] #not entirely sure why I made this a seperate function but eh
|
||||
|
||||
def isNumber(data):
|
||||
if data == '':
|
||||
return True
|
||||
try:
|
||||
rv = int(data)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def formatExportWeapons(self, file1):
|
||||
log = open("log.txt", "a")
|
||||
testfile = open("test1.json", 'w')
|
||||
if open(file1):
|
||||
print("opened")
|
||||
with open(file1, 'r') as file:
|
||||
data=json.load(file)["ExportWeapons"]
|
||||
redund = []
|
||||
for diction in range(len(data)):
|
||||
|
||||
for key in list(data[diction].keys()):
|
||||
if key not in ["name", "slot", "uniqueName"]:
|
||||
del data[diction][key]
|
||||
print("deleted " + str(data[diction]["name"] + ' ' + key))
|
||||
if 'slot' not in data[diction].keys():
|
||||
redund.append(diction)
|
||||
|
||||
i=0
|
||||
for diction in redund:
|
||||
|
||||
i+=1
|
||||
print("Removed Unneeded Modular " + str(data[diction-i]["name"]), file=log)
|
||||
del data[diction-i]
|
||||
|
||||
def sortFunc(e):
|
||||
return e["name"]
|
||||
|
||||
data.sort(key=sortFunc)
|
||||
|
||||
json.dump(data, testfile, indent=2)
|
||||
return data
|
||||
|
||||
def formatExportPowersuits(self, file1):
|
||||
log = open("log.txt", "a")
|
||||
testfile = open("test2.json", 'w')
|
||||
if open(file1):
|
||||
print("opened")
|
||||
with open(file1, 'r') as file:
|
||||
data=json.load(file)["ExportWarframes"]
|
||||
redund = []
|
||||
for diction in range(len(data)):
|
||||
|
||||
for key in list(data[diction].keys()):
|
||||
if key not in ["name", "productCategory", "uniqueName"]:
|
||||
del data[diction][key]
|
||||
print("deleted " + str(data[diction]["name"] + ' ' + key))
|
||||
|
||||
|
||||
def sortFunc(e):
|
||||
return e["name"]
|
||||
|
||||
data.sort(key=sortFunc)
|
||||
|
||||
json.dump(data, testfile, indent=2)
|
||||
return data
|
||||
|
||||
def formatExportPlus(self, file1):
|
||||
with open(file1, 'r') as file:
|
||||
data=json.load(file)
|
||||
write = False
|
||||
for item in data.keys():
|
||||
print(data[item])
|
||||
if type(data[item]) is dict:
|
||||
icon = data[item]["icon"]
|
||||
print(data[item])
|
||||
data[item] = icon
|
||||
write=True
|
||||
|
||||
if write == True:
|
||||
with open(file1, 'w') as file:
|
||||
json.dump(data, file, indent=2)
|
||||
|
||||
return data
|
||||
|
||||
def getIcon(self, path, savePath):
|
||||
try:
|
||||
image = Image.open("./cache/items/icons" + savePath)
|
||||
return image
|
||||
except:
|
||||
getIconDownload(self, path, savePath)
|
||||
return Image.open("./cache/items/icons" + savePath)
|
||||
#Old Helper options, left here for future reference. Shitty code, but it works, and it may be useful in the future
|
||||
|
||||
# def getlogmax():
|
||||
# file = 'sngconfig.json'
|
||||
# if Path(file).is_file():
|
||||
# with open(file) as f:
|
||||
# d = json.load(f)
|
||||
#
|
||||
# else:
|
||||
# return False
|
||||
|
||||
|
||||
|
||||
# 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#
|
||||
|
||||
|
23
subfiles/install.py
Normal file
23
subfiles/install.py
Normal file
@ -0,0 +1,23 @@
|
||||
def check():
|
||||
print("install Good")
|
||||
|
||||
|
||||
def installFiles(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!")
|
||||
|
55
subfiles/savetools.py
Normal file
55
subfiles/savetools.py
Normal file
@ -0,0 +1,55 @@
|
||||
import json
|
||||
|
||||
def check():
|
||||
print("Save Check Good")
|
||||
|
||||
# Yea I know this is pretty pointless in a seperate file but it's nice to have these things
|
||||
def loadFile(self, file1):
|
||||
with open(file1, "r") as file:
|
||||
|
||||
data = json.load(file)
|
||||
return data
|
||||
|
||||
def saveFile(self, newData, file):
|
||||
with open(file, "r") as file1:
|
||||
data = json.load(file1)
|
||||
def anotherRecusion(data, newData):
|
||||
if type(data) is dict:
|
||||
for key in newData:
|
||||
match data[key]:
|
||||
case str():
|
||||
data[key]=newData[key].get()
|
||||
case bool():
|
||||
data[key]=newData[key].get()
|
||||
case int():
|
||||
data[key]=int(newData[key].get())
|
||||
case dict():
|
||||
data[key] = anotherRecusion(data[key], newData[key])
|
||||
case list():
|
||||
data[key]= anotherRecusion(data[key], newData[key])
|
||||
elif type(data) is list:
|
||||
for key in range(len(newData)):
|
||||
match data[key]:
|
||||
case str():
|
||||
data[key]=newData[key].get()
|
||||
case bool():
|
||||
data[key]=newData[key].get()
|
||||
case int():
|
||||
data[key]=int(newData[key].get())
|
||||
case dict():
|
||||
data[key] = anotherRecusion(data[key], newData[key])
|
||||
case list():
|
||||
data[key]= anotherRecusion(data[key], newData[key])
|
||||
return data
|
||||
data = anotherRecusion(data, newData)
|
||||
print(data)
|
||||
with open(file, "w") as file1:
|
||||
json.dump(data, file1, indent=2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
44
subfiles/themes.py
Normal file
44
subfiles/themes.py
Normal file
@ -0,0 +1,44 @@
|
||||
def getThemes(self):
|
||||
themes = {
|
||||
"black_orange" : {
|
||||
"fg_color" : "#000000",
|
||||
"bg_color" : "#14213D",
|
||||
"bg_deep" : "#121e37",
|
||||
"border_color" : "#14213D",
|
||||
"highlight_color" : '#FCA311',
|
||||
"text_color" : ""
|
||||
},
|
||||
"kiln" : {
|
||||
"fg_color" : "#5A2901",
|
||||
"bg_color" : "#AE6F3B",
|
||||
"bg_deep" : "#885599",
|
||||
"border_color" : "#AE6F3B",
|
||||
"highlight_color" : '#246868',
|
||||
"text_color" : ""
|
||||
},
|
||||
"mint_nebula" : {
|
||||
"fg_color" : "#15001F",
|
||||
"bg_color" : "#2F0047",
|
||||
"bg_deep" : "#440066",
|
||||
"border_color" : "#6B3E00",
|
||||
"highlight_color" : '#00683E',
|
||||
"text_color" : ""
|
||||
},
|
||||
"antimatter" : {
|
||||
"fg_color" : "#13002C",
|
||||
"bg_color" : "#2F036A",
|
||||
"bg_deep" : "#22014E",
|
||||
"border_color" : "#2F026A",
|
||||
"highlight_color" : '#9C8E00',
|
||||
"text_color" : ""
|
||||
},
|
||||
"fenix" : {
|
||||
"fg_color" : "#451D08",
|
||||
"bg_color" : "#73300D",
|
||||
"bg_deep" : "#73300D",
|
||||
"border_color" : "##A14312",
|
||||
"highlight_color" : '#083045',
|
||||
"text_color" : ""
|
||||
}
|
||||
}
|
||||
return themes
|
Loading…
x
Reference in New Issue
Block a user