Last active
October 29, 2023 01:35
-
-
Save AntumDeluge/80b4877db9afe1a2af259c3210d87b00 to your computer and use it in GitHub Desktop.
A script for retrieving the current Stendhal test client.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# This file is licensed under CC0. | |
# | |
# A script for retrieving the Stendhal Java client. | |
# | |
# This script requires Python to be installed on your system. | |
# Tested with Python versions 2.7, 3.6, & 3.7. If you would like | |
# download progress displayed, install the "wget" module. The | |
# "wget" module can be installed via Pypi: | |
# `pip install wget` or `python -m pip install wget` | |
import errno, os, re, shutil, stat, subprocess, sys, zipfile | |
from platform import system | |
from subprocess import PIPE, STDOUT | |
py_legacy = sys.version_info.major <= 2 | |
if not py_legacy: | |
import urllib.request as urllib2 | |
from urllib.error import HTTPError | |
raw_input = input | |
module_error = ModuleNotFoundError | |
else: | |
import commands, urllib2 | |
from urllib2 import HTTPError | |
module_error = ImportError | |
try: | |
import wget | |
except module_error: | |
print("WARNING:\twget module not found!") | |
print("\t\tDownload progress information will not be available.") | |
print("\t\tIf you would like progress info, install the wget\n\t\tmodule via Pypi:") | |
print("\t\t\t`pip install wget` or `python -m pip install wget`\n") | |
# system information | |
win32 = (system().lower() == "windows") | |
# script information | |
script_version = "1.3" | |
script_name = os.getenv("SCRIPT_NAME") or os.path.basename(__file__) | |
## Prints version information. | |
def showVersion(): | |
print(script_version) | |
## Prints usage information. | |
def showUsage(): | |
print("\nDescription:\n" \ | |
+ " Script to retrieve current Stendhal testing client.\n" \ | |
+ "\nVersion:\n" \ | |
+ " {}\n".format(script_version) \ | |
+ "\nUsage:\n" \ | |
+ " {} [<options>] [<version>]\n".format(script_name) \ | |
+ "\nArguments:\n" \ | |
+ " options:\n" \ | |
+ " -h|--help ..... Show this help information.\n" \ | |
+ " -v|--version .. Show version information.\n" \ | |
+ " -c|--clean .... Clean files associated with Stendhal's\n" \ | |
+ " client from the extract directory\n" \ | |
+ " (default: current directory).\n" \ | |
+ " -e|--extract .. Directory where contents of download\n" \ | |
+ " should be extracted (default: current\n" \ | |
+ " directory).\n" \ | |
+ " version:\n" \ | |
+ " The target version. If this is omitted, the script will\n" \ | |
+ " try to detect the latest version from Stendhal's GitHub\n" \ | |
+ " repository.") | |
args = sys.argv[1:] | |
unknown_args = [] | |
params = { | |
"show_u": False, | |
"show_v": False, | |
"version": None, | |
"testclient": False, | |
"clean": False, | |
"d_download": None, | |
"d_extract": os.getcwd(), | |
} | |
def argParamError(p): | |
print("\nERROR: `{}` parameter requires an argument".format(p)) | |
sys.exit(1) | |
while len(args) > 0: | |
a = args[0].lower() | |
if a in ("-h", "--help", "help", "-?", "/?"): | |
params["show_u"] = True | |
elif a in ("-v", "--version"): | |
params["show_v"] = True | |
elif a in ("-t", "--testclient"): | |
params["testclient"] = True | |
elif a in ["-c", "--clean", "clean"]: | |
params["clean"] = True | |
elif a in ["-d", "--download"]: | |
if len(args) < 2 or args[1].startswith("-"): | |
argParamError("download") | |
params["d_download"] = args[1] | |
args.pop(1) | |
elif a in ["-e", "--extract"]: | |
if len(args) < 2 or args[1].startswith("-"): | |
argParamError("extract") | |
params["d_extract"] = args[1] | |
args.pop(1) | |
elif not params["version"]: | |
params["version"] = a | |
else: | |
unknown_args.append(a) | |
args.pop(0) | |
if len(unknown_args): | |
print("\nERROR: Unknown argument: {}".format(unknown_args[0])) | |
sys.exit(1) | |
if params["show_u"]: | |
showUsage() | |
sys.exit(0) | |
if params["show_v"]: | |
showVersion() | |
sys.exit(0) | |
if params["clean"]: | |
cont = (raw_input("\nWARNING: This will delete files from the" \ | |
+ " directory ({}). Continue? [y/N]: ".format(params["d_extract"])) \ | |
.lower() in ("y", "yes")) | |
if not cont: | |
print("Exiting ...") | |
sys.exit(0) | |
clean_files = ("stendhal-starter.jar", "stendhal-starter.exe", "icon.png", "README.md") | |
clean_exts = ("zip") | |
clean_dirs = ("doc", "lib", "log", "launch4j") | |
rmfiles = [] | |
rmdirs = [] | |
for filename in os.listdir(params["d_extract"]): | |
filepath = os.path.join(params["d_extract"], filename) | |
if os.path.isfile(filepath): | |
if filename in clean_files: | |
rmfiles.append(filepath) | |
continue | |
if "." in filename: | |
ext = filename.split(".")[-1] | |
if ext in clean_exts: | |
rmfiles.append(filepath) | |
continue | |
if os.path.isdir(filepath): | |
if filename in clean_dirs: | |
rmdirs.append(filepath) | |
if rmfiles or rmdirs: | |
print("Cleaning files & directories ...") | |
for filepath in rmfiles: | |
print("DEL:\t {}".format(filepath)) | |
os.remove(filepath) | |
for dirpath in rmdirs: | |
print("DEL DIR: {}".format(dirpath)) | |
shutil.rmtree(dirpath) | |
else: | |
print("No files or directories found to be cleaned.") | |
sys.exit(0) | |
## Converts the value of an environment variable to a tuple. | |
# | |
# Delimited by ";" on Windows, ":" otherwise. | |
# | |
# @param e | |
# Environment variable name to be expanded. | |
# @return | |
# Tuple. | |
def envToList(e): | |
e = os.getenv(e) | |
if not e: | |
return () | |
if win32: | |
return tuple(e.split(";")) | |
return tuple(e.split(":")) | |
## Retrieves the first usable path from a list. | |
# | |
# @param default | |
# The path to use if none found from <code>group</code>. | |
# @param group | |
# List of paths to be checked for usability. | |
# @return | |
# The first usable path or <code>default</code>. | |
def selectWritableDir(default, group): | |
for d in group: | |
if d: | |
d = os.path.normpath(d) | |
if os.access(d, os.W_OK): | |
return d | |
default = os.path.normpath(default) | |
if not os.access(default, os.W_OK): | |
print("WARNING: using non-writable default directory: {}".format(default)) | |
return default | |
## Checks write permissions of directory tree. | |
# | |
# @param d | |
# Directory tree to be checked. | |
def checkWriteTree(d, action="download"): | |
while d.strip() and not os.path.isdir(d): | |
d = os.path.dirname(d) | |
if not os.access(d, os.W_OK): | |
print("\nERROR: Access not granted to {} to {}".format(action, d)) | |
sys.exit(errno.EPERM) | |
## Checks if a command is available. | |
# | |
# @param cmd | |
# The executable basename to search for. | |
# @return | |
# The command name if found. | |
def which(cmd): | |
if os.access(cmd, os.X_OK): | |
return cmd | |
# search PATH | |
PATH = envToList("PATH") | |
PATHEXT = envToList("PATHEXT") | |
for d in PATH: | |
filepath = os.path.join(d, cmd) | |
if os.access(filepath, os.X_OK): | |
return filepath | |
for ext in PATHEXT: | |
if not ext.startswith("."): | |
ext = "." + ext | |
for e in (ext, ext.lower(), ext.upper()): | |
filepath_ext = filepath + e | |
if os.access(filepath_ext, os.X_OK): | |
return filepath_ext | |
return None | |
usable_dirs = ( | |
os.getenv("HOME"), | |
os.getenv("HOMEPATH"), | |
os.getenv("USERPROFILE"), | |
) | |
dir_home = selectWritableDir(os.path.expanduser("~"), usable_dirs) | |
# wget lib takes precedence | |
have_lib_wget = "wget" in globals() | |
cmd_wget = None | |
if not have_lib_wget: | |
cmd_wget = which("wget") | |
## Downloads a file from a URL. | |
# | |
# @param url | |
# The URL to fetch from. | |
# @param target | |
# The target download directory. | |
def download(url, target=None): | |
if not target: | |
print("\nERROR: must specify a download target") | |
sys.exit(1) | |
dir_parent = os.path.dirname(target) | |
if not os.path.isdir(dir_parent): | |
os.makedirs(dir_parent) | |
if have_lib_wget: | |
try: | |
wget.download(url, target) | |
print("\n") | |
except HTTPError: | |
print("\nERROR: Resource for Stendhal client version {} not found on server. Exiting ...".format(params["version"])) | |
sys.exit(errno.ENOENT) | |
elif cmd_wget != None: | |
# there are some problems with showing progress info with the wget executable | |
#execute_cmd = [cmd_wget, "--show-progress", url] | |
execute_cmd = [cmd_wget, "-q", url] | |
if target: | |
execute_cmd.insert(len(execute_cmd) - 1, "-O") | |
execute_cmd.insert(len(execute_cmd) - 1, target) | |
if py_legacy: | |
ps = subprocess.Popen(execute_cmd, stderr=subprocess.PIPE) | |
ps.communicate() | |
output = None | |
exit_res = ps.returncode | |
else: | |
exit_res, output = subprocess.getstatusoutput(" ".join(execute_cmd)) | |
if exit_res != 0: | |
# remove invalid zip created by wget | |
if os.path.isfile(target): | |
os.remove(target) | |
print("\nERROR: Could not retrieve resource for Stendhal client version {} (return code {}). No other information available. Exiting ...".format(params["version"], exit_res)) | |
sys.exit(exit_res) | |
else: | |
# either wget command nor library available | |
print("\nwget library and wget executable not found. Using built-in Python libraries for downloading.") | |
file_name = os.path.basename(url) | |
if target == None: | |
target = os.getcwd() | |
try: | |
dl_open = urllib2.urlopen(url) | |
dl_data = dl_open.read() | |
dl_open.close() | |
if os.path.isdir(target): | |
target = os.path.join(target, file_name) | |
fopen = open(target, "wb") | |
fopen.write(dl_data) | |
fopen.close() | |
except HTTPError: | |
print("\nERROR: Resource for Stendhal client version {} not found on server. Exiting ...".format(params["version"])) | |
sys.exit(errno.ENOENT) | |
if not params["version"]: | |
ant_prop_url = "https://raw.githubusercontent.com/arianne/stendhal/master/build.ant.properties" | |
print("Stendhal version not specified, attempting to retrieve automatically from URL:\n\t\t{}".format(ant_prop_url)) | |
try: | |
prop_open = urllib2.urlopen(ant_prop_url) | |
prop = prop_open.read().decode("utf-8").split("\n"); | |
prop_open.close(); | |
vdelim = "version" | |
if not params["testclient"]: | |
vdelim = vdelim + ".old" | |
for line in prop: | |
line = re.sub(" ", "", line) | |
if line.startswith(vdelim + "="): | |
params["version"] = line.split("=")[1] | |
if not params["version"]: | |
print("\nERROR:\n" \ | |
+ " Could not parse version information from URL:\n" \ | |
+ " {}".format(ant_prop_url) + "\n" \ | |
+ " Please specify version number manually: {} <version>".format(script_name)) | |
sys.exit(1) | |
except HTTPError: | |
print("\nERROR:\n" \ | |
+ " Could not retrieve version information from URL:\n" \ | |
+ " {}".format(ant_prop_url) + "\n" \ | |
+ " Please specify version number manually: {} <version>".format(script_name)) | |
sys.exit(1) | |
file_zip = "stendhal-{}.zip".format(params["version"]) | |
if params["testclient"]: | |
client_url = "https://stendhalgame.org/download/{}".format(file_zip) | |
else: | |
client_url = "https://sourceforge.net/projects/arianne/files/stendhal/{0}/{1}/download".format(params["version"], file_zip) | |
# set up download directory (use current directory if no other useable target) | |
useable_dirs = [ | |
os.getenv("TMP"), | |
os.getenv("TEMP"), | |
"/tmp", | |
"{}/Downloads".format(dir_home), | |
"{}/Downloads".format(os.getenv("USERPROFILE"))] | |
if not params["d_download"]: | |
params["d_download"] = selectWritableDir(os.getcwd(), useable_dirs) | |
checkWriteTree(params["d_download"]) | |
checkWriteTree(params["d_extract"], "extract") | |
file_download = os.path.join(params["d_download"], file_zip) | |
print("\nVersion: {}\n".format(params["version"]) \ | |
+ "Source: {}\n".format(client_url) \ | |
+ "Target: {}\n".format(file_download)) | |
## Gets prompt to overwrite download target .zip archive. | |
def overwriteDownloadTargetZip(url, target): | |
cont = True | |
fexists = os.path.isfile(target) | |
if fexists: | |
cont = (raw_input("Overwrite existing file? [y/N]: ").lower() in ("y", "yes")) | |
if cont: | |
if not os.access(target, os.W_OK): | |
print("\nERROR: Access not granted to delete file {}".format(target)) | |
sys.exit(errno.EPERM) | |
os.remove(target) | |
if cont: | |
print("Downloading file from {}".format(url)) | |
download(url, target) | |
elif fexists: | |
cont = raw_input("Extract contents anyway? [y/N]: ").lower() in ("y", "yes") | |
return cont | |
## Extracts a .zip archive. | |
# | |
# @param source | |
# File path. | |
def extractZip(source): | |
if not os.path.isfile(source): | |
print("ERROR: File not found: {}".format(source)) | |
return False | |
print("Extracting to: {}".format(params["d_extract"])) | |
try: | |
archive = zipfile.ZipFile(source, "r") | |
archive.extractall(params["d_extract"]) | |
archive.close() | |
except zipfile.BadZipfile: | |
print("\nERROR: .zip file is corrupt: {}".format(source)) | |
sys.exit(errno.EBADFD) | |
# get icon | |
ico = "{}/icon.png".format(params["d_extract"]) | |
if not os.path.isfile(ico): | |
print("Downloading icon ...") | |
download("https://github.com/arianne/stendhal/raw/master/data/gui/StendhalIcon.png", ico) | |
return True | |
# download & extract Stendhal client .zip | |
if overwriteDownloadTargetZip(client_url, file_download): | |
extractZip(file_download) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment