Compare commits

...

2 Commits

Author SHA1 Message Date
BodgeMaster 0260d13ac1 prepared total rewrite (AGAIN!!!) 2020-03-22 21:35:57 +01:00
BodgeMaster 16fe54de9e replaced old fake java 2020-03-22 21:29:22 +01:00
17 changed files with 17 additions and 390 deletions

View File

@ -2,40 +2,16 @@
The Launcher for my mod pack
# General TODO List
* [ ] Frontend
* [ ] Port the GUI (and fix the design differences between Desktop environments) or write a new one (→ `gui.py`)
* [ ] Write a CLI / GUI hybrid program to handle the libraries (→ `main.py`)
* [ ] Library development
* [ ] Write a new game launching Library (implementation started) (→ `mchandler.py`)
* [x] Write a new authentication library (→ `yggdrasil.py`)
* [ ] Write a config handler (important features done) (→ `cfghandler.py`)
* [ ] Create server startup request library
* [ ] Modpack update helper (→ `updater.py`)
* [ ] Develop server side tools
* [ ] Server startup request listener
* [ ] Modpack update helper
* [ ] Web files (HTML files to be shown in and cached by the launcher)
* [ ] Server status page
* [ ] News page
* [ ] Credits page
* [ ] License!?
*[ ] Everything
*[ ] Make a ToDo list and possibly create issues
# Documentation
Documentation for each individual file can be found in the same directory
## Directories
* __devtools__ contains development tools
* __devtools__ contains custom development tools
* __lib__ contains libraries for Minecraft and the launcher
* __res__ contains the resources/assets used by Minecraft
* __run__ contains the running directory / game directory
## Files
* __main.py__ the main file of the Launcher
# TODO list
* __main.py__
* [ ] run the updater
* [ ] CLI
* [ ] GUI
* [ ] decide whether to launch the GUI
* [ ] launch GUI (from `gui.py`)
* [ ] get user input from GUI
* [ ] Act according to the user input
# Per-File TODO list

View File

@ -1,4 +1,4 @@
# Development tools
## Files:
* __fake_java.py__ can be used to get the command used to start Minecraft. When run, it will create a file called `fake_java_out.txt` in its working directory.
* __harvest_arguments.sh__ can be used to get the command used to start Minecraft. Just rename the Java executable to <name>_backup and put this file in its place. Output files will be created in the running directory. They are named by the current date.
* __rmpyc__ removes all `.pyc` files in the current working directory and all subdirectories.

View File

@ -1,12 +0,0 @@
#!/usr/bin/python2
import sys
first_argument=True
out=open("fake_java_out.txt", "w")
for element in range(len(sys.argv)):
out.write(sys.argv[element])
first_argument=False
if not first_argument:
out.write(" ")
out.close()

10
devtools/harvest_arguments.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
NOW=`date`
echo "Executable: $0" > "$NOW.log"
echo "Timestamp: $NOW" >> "$NOW.log"
echo -n "Arguments: " >> "$NOW.log"
echo "$*" | sed ':a;s/ -/\n >>>-/;ta' >> "$NOW.log"
echo "Full command: $0 $*" >> "$NOW.log"
BACKUPCMD="`readlink -f \`which $0\``_backup"
echo "Actual command: $BACKUPCMD $*" >> "$NOW.log"
echo "$BACKUPCMD $*" | sh

View File

@ -1,6 +1,6 @@
# lib
This directory contains libraries, natives and runtimes used by the launcher and Minecraft.
## Directories
* __jar__ the libraries for minecraft (and the version jar...)
* __jar__ the libraries for minecraft (and the version jar cause its on the cp as well...)
* __launcher__ everything used by the launcher
* __natives__ the natives for minecraft

View File

@ -1,5 +1,3 @@
# lib/launcher
This directory contains the libraries and runtimes used by the launcher.
##Directories
* __java__ the Java components
* __python__ the python components

View File

@ -1,3 +0,0 @@
# lib/launcher/java
##Directories
* __runtime__ will contain a java runtime

View File

@ -1,2 +0,0 @@
# libs/launcher/java/runtime
I intend to package a java runtime with the launcher as it helps to prevent some issues. For instance, Minecraft runs only on Java 8 and below. If the default JRE on the operating systems path is not Java 8, Minecraft will not even launch sucessfully.

View File

@ -1,52 +0,0 @@
# lib/launcher/python
## Directories:
* __runtime__ will contain a python runtime
## Files
* __gui.py__ will contain the GUI (Wow! Really?). The GUI will be accessed by `main.py`
* __mchandler.py__ will be the launcher library. This time, it will not be a runnable hybrid library
* __updater.py__ will be used for updating the mod pack and possibly the launcher as well
* __cfghandler.py__ will deal with config files
* __yggdrasil.py__ is a nearly feature-complete Minecraft authentication library
# TODO list
* __gui.py__
* [ ] add a function to open the GUI
* [ ] add functions for reading values from the GUI
* [ ] add functions for writing values to the GUI
* [ ] put the port of the GUI here or in a new file?
* __mchandler.py__
* [ ] add a launch function using the access token
* ~~add a function to verify a given access token~~ _moved to `yggdrasil.py`_
* [x] add functtions to pass configuration data
* ~~launch command (with escape sequences for non-static values (configuration and user data)~~ _moved_
* ~~RAM amount~~
* ~~Game Directory~~
* ~~Additional arguments~~
* _handled by a dictionary_
* [ ] add the launch command builder
* [ ] add the launch command setter
* [x] add RAM auto detection
* [ ] add proper error handling
* __updater.py__
* [ ] add a function to determine whether updates are available
* [ ] add a function to download updates
* [ ] add a function to update the launcher
* [ ] add a function to update the mod pack
* [ ] add different types of updates
* [ ] required launcher updates
* [ ] required mod pack updates
* [ ] optional launcher updates
* [ ] optional mod pack updates
* [ ] setup change updates (used to install/uninstall components)
* [ ] add initial mod pack install by applying a setup change update **→** main.py
* __cfghandler.py__
* [x] provide read and write functions for config values
* [x] add a function to set the config file path
* [x] add functions to load and store the configuration to the file
* [ ] implement functions for config updates
* [ ] raise adequate exceptions where needed
* __yggdrasil.py__
* [x] add a function to verify user credentials and get the access token
* [x] add a function to verify a given acces token

View File

@ -1,125 +0,0 @@
import json, os, errno
# self-explanatory
default_config = {}
# This will contain the current configuration after loading the file and
# combining it with the default configuration. All changes will be done here
config = None
# The file to work with
config_file_path = None
################################################################################
# file interaction
################################################################################
# set the config file to use
def set_file_path(file_path):
global config_file_path
# error handling
if os.path.exists(file_path):
if os.path.isdir(file_path):
raise IOError(errno.EISDIR, "Is a directory", file_path)
if os.access(file_path, os.R_OK) and os.access(file_path, os.W_OK):
pass
else:
raise IOError(errno.EACCES, "Read AND write permissions required", file_path)
else:
config_file = open(file_path, "w")
config_file.write("{}")
config_file.close()
# actually setting the file path
config_file_path = file_path
# loads the json file at file_path and updates the default configuration with it
# overwrites any existing configuration adjustments
# TODO: handle None as file name (raise an exception)
def load():
global config_file_path
global config
global default_config
# read the config file
config_file = open(config_file_path,"r")
config_string = config_file.read()
config_file.close()
# combine with default config
config = default_config.copy()
config.update(json.loads(config_string))
# stores the current configuration to the file at file_path
# TODO: handle None (raise an exception)
def store():
global config
global config_file_path
if not config is None:
config_file = open(config_file_path, "w")
config_file.write(json.dumps(config))
config_file.close()
################################################################################
# default config / config setup
# These functions should be used BEFORE loading the config file
################################################################################
#defines a new option group in the default config
# TODO: handle existing groups (raise an exception)
def define_group(group):
global default_config
default_config.update({group : {}})
# defines a new option in the default config
# TODO: handle existing options (raise an exception)
def define_option(group, name, default_value):
global default_config
default_config[group].update({name : default_value})
# self-explanatory
def clear_definitions():
global default_config
default_config = {}
################################################################################
# adjust an existing configuration for new needs
################################################################################
# remove the given option from the configuration and return the value to allow
# for conversion if desired
# TODO: implement
def remove_option(group, name):
pass
# move the value of an option to another place
# TODO: implement
def rename_option(old_group, old_name, new_group, new_name):
pass
# remove an entire group from the configuration, never to be seen again
# TODO: implement
def remove_group(group):
pass
# self-explanatory
# TODO: implement
def rename_group(old_group, new_group):
pass
################################################################################
# config interaction
################################################################################
# get the value of the given option
def fetch(group, name):
global config
if config is None:
load()
return config[group][name]
# set the value of a config option
def set(group, name, value):
global config
if config is None:
load()
config[group][name] = value

View File

@ -1,68 +0,0 @@
import os, psutil
# java -Xmx$RAM$ -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:-UseAdaptiveSizePolicy -Xmn128M -Djava.library.path=$NATIVES$ -cp $LIBRARIES$ net.minecraft.launchwrapper.Launch --username $USERNAME$ --version 1.7.10-Forge10.13.4.1614-1.7.10 --gameDir $GAMEDIR$ --assetsDir $ASSETS$ --assetIndex 1.7.10 --uuid $UUID$ --accessToken $ACCESSTOKEN$ --userProperties {} --userType mojang --width $WINDOWWIDTH$ --height $WINDOWHEIGHT$ --tweakClass cpw.mods.fml.common.launcher.FMLTweaker
# $RAM$ the amount of RAM with postfix (k, M, G ...)
# $WINDOWWIDTH$, $WINDOWHEIGHT$ window width and height in pixels without the WMs decoration
# $USERNAME$ the username as shown in-game
# $UUID$ the players uuid
# $ACCESSTOKEN$ a valid accessToken
# $NATIVES$ the directory containing the natives
# $LIBRARIES$ a colon-separated (semicolon-separated on Windowze) list of library jars
# $GAMEDIR$ the game directory IMPORTANT! Change the cwd to that directory before launch. Some mods are coded to find the standard paths by using the cwd.
# $ASSETS$ the directory containing the assets
game_properties = {
"window width": "1366",
"window height": "768",
"ram": "4G",
"username": None,
"uuid": None,
"access token": None,
"native directory": "lib/natives",
"library directory": "lib/jar", # will be transformed by the launch function
"asset directory": "res",
"game directory": "run",
"java executable": "java",
"additional arguments": ""}
command = None
full_command = None
def autoram(minram, maxram, balance):
ram_available = psutil.virtual_memory().available
# not enough memory
if ram_available < minram:
# TODO: raise an exception
return -1
# plenty of memory
elif ram_available > maxram+balance:
return maxram
# enough memory but we do not want to occupy all
elif float(ram_available)*float(maxram)/float(maxram+balance) > minram:
return int(float(ram_available)*float(maxram)/float(maxram+balance))
# barely enough memory
elif ram_available > minram or ram_available == minram:
return ram_available
def launch():
global command
if command is None:
return 1
# TODO: Raise an exception
return os.system(command)
def set_parameter(parameter, value):
global game_propertoies
game_properties[parameter] = value
build_full_command()
def build_full_command():
global game_properties
global command
global full_command
# TODO: implement
def set_command(minecraft_command):
global command
command = minecraft_command
build_full_command()

View File

@ -1,2 +0,0 @@
# lib/launcher/python/runtime
I intend to ship the launcher with a Python runtime included because not all of the Windowze noobs out there will want to install Python themselves.

View File

@ -1,49 +0,0 @@
import json, urllib2
################################################################################
# This software can be used privately for any purpose.
# It may be shared publickly as long as you give credit and provide
# a link to the original source.
# Do not use this for malicious purposes.
################################################################################
# This allows for easier addition of new actions
discardResponse = {
"validate": True,
"invalidate": True,
"signout": True,
"authenticate": False,
"refresh": False
}
# Interaction with the Mojang authentication server
def interact(action, data):
if discardResponse[action]:
try:
urllib2.urlopen(urllib2.Request(url="https://authserver.mojang.com/"+action, data=json.dumps(data).encode(),headers={"Content-Type": "application/json"})).read().decode()
return True, {}
except urllib2.HTTPError:
return False, {}
else:
try:
serverResponse = json.loads(urllib2.urlopen(urllib2.Request(url="https://authserver.mojang.com/"+action, data=json.dumps(data).encode(),headers={"Content-Type": "application/json"})).read().decode())
return True, serverResponse
except urllib2.HTTPError:
return False, {}
# Wrapper functions for interact
def authenticateUser(logname, passwd):
return interact("authenticate", {'username': logname, 'password': passwd, 'agent': {'version': 1, 'name': 'Minecraft'}})
def validateToken(accessToken, clientToken):
return interact("validate", {'accessToken': accessToken, 'clientToken': clientToken})
def refreshToken(accessToken, clientToken):
return interact("refresh", {'accessToken': accessToken, 'clientToken': clientToken})
def deauthenticateUser(logname, passwd):
return interact("signout", {'username': logname, 'password': passwd})
def invalidateToken(accessToken, clientToken):
return interact("invalidate", {'accessToken': accessToken, 'clientToken': clientToken})

44
main.py
View File

@ -1,44 +0,0 @@
#!/usr/bin/python2
#proposed launcher startup sequence
#
# - look at command line arguments
# possible arguments:
# --ui:gui, --ui:cli-interactive, --ui:args-only, --ui:auto (default: auto)
# --update:if-available, --update:force, --update:noupdate (default: if-available)
# --launch, -l (default: not present)
# --silent, --quiet, -s, -q (default: only present in interactive cli mode)
#
# - decide whether to launch the GUI or not (either by command line argument
# or by determining if running from terminal)
#
# - if updating: install update and restart if in GUI mode or interactive CLI mode,
# update and exit in args-only mode
#
# - launch GUI or launch interactive CLI or follow instructions from the
# command line:
# If --ui:args-only is present, the default actions are update:if-available
# and exit without launching. If -l is present, the GUI or
# interactive CLI are only shown while updating and Minecraft will launch
# automatically.
#
# - load configuration
#
# - handle user actions (only in GUI or interactive mode, skipped with -l)
# - update and store configuration
# - GUI interaction
# - wait for launch or close actions
#
# - launch the game or exit
# import standard libraries
import sys
# adjust path
sys.path.append("./lib/launcher/python")
# import libraries from new path
import yggdrasil, cfghandler, mchandler
# temporary test stuff
a,b = yggdrasil.authenticateUser(raw_input("Username: "), raw_input("Password: "))
if a:
print b["accessToken"]

View File

@ -2,4 +2,4 @@
This directory will contain the resources used by Minecraft.
## Files
* __gather_assets.sh__ will conveniently place all your Minecraft assets here for you.
* __gather_assets.sh__ will conveniently place all your Minecraft assets here for you. [Should be moved to devtools...]