2021-05-15 23:16:51 +00:00
|
|
|
import base64
|
2022-10-16 23:08:31 +00:00
|
|
|
import json
|
2023-03-20 16:01:08 +00:00
|
|
|
import pickle
|
2022-10-16 23:08:31 +00:00
|
|
|
import typing
|
2021-09-17 23:02:26 +00:00
|
|
|
import uuid
|
2022-10-16 23:08:31 +00:00
|
|
|
import zipfile
|
2023-03-20 16:01:08 +00:00
|
|
|
import zlib
|
2020-06-26 17:29:33 +00:00
|
|
|
|
2023-03-20 16:01:08 +00:00
|
|
|
from io import BytesIO
|
2022-12-05 23:40:51 +00:00
|
|
|
from flask import request, flash, redirect, url_for, session, render_template, Markup
|
2023-03-20 16:01:08 +00:00
|
|
|
from pony.orm import commit, flush, select, rollback
|
|
|
|
from pony.orm.core import TransactionIntegrityError
|
2020-06-26 17:29:33 +00:00
|
|
|
|
2022-10-16 23:08:31 +00:00
|
|
|
import MultiServer
|
|
|
|
from NetUtils import NetworkSlot, SlotType
|
2022-09-29 22:36:30 +00:00
|
|
|
from Utils import VersionException, __version__
|
|
|
|
from worlds.Files import AutoPatchRegister
|
2022-10-16 23:08:31 +00:00
|
|
|
from . import app
|
2023-03-20 16:01:08 +00:00
|
|
|
from .models import Seed, Room, Slot, GameDataPackage
|
2020-06-26 17:29:33 +00:00
|
|
|
|
2022-11-20 19:39:52 +00:00
|
|
|
banned_zip_contents = (".sfc", ".z64", ".n64", ".sms", ".gb")
|
2020-06-26 17:29:33 +00:00
|
|
|
|
|
|
|
|
2021-09-17 23:02:26 +00:00
|
|
|
def upload_zip_to_db(zfile: zipfile.ZipFile, owner=None, meta={"race": False}, sid=None):
|
|
|
|
if not owner:
|
|
|
|
owner = session["_id"]
|
|
|
|
infolist = zfile.infolist()
|
2022-12-05 23:40:51 +00:00
|
|
|
if all(file.filename.endswith((".yaml", ".yml")) or file.is_dir() for file in infolist):
|
|
|
|
flash(Markup("Error: Your .zip file only contains .yaml files. "
|
|
|
|
'Did you mean to <a href="/generate">generate a game</a>?'))
|
|
|
|
return
|
2022-09-28 21:54:10 +00:00
|
|
|
slots: typing.Set[Slot] = set()
|
2021-09-17 23:02:26 +00:00
|
|
|
spoiler = ""
|
2022-11-20 19:39:52 +00:00
|
|
|
files = {}
|
2021-09-17 23:02:26 +00:00
|
|
|
multidata = None
|
2022-11-20 19:39:52 +00:00
|
|
|
|
|
|
|
# Load files.
|
2021-09-17 23:02:26 +00:00
|
|
|
for file in infolist:
|
2022-03-18 03:53:09 +00:00
|
|
|
handler = AutoPatchRegister.get_handler(file.filename)
|
2021-09-17 23:02:26 +00:00
|
|
|
if file.filename.endswith(banned_zip_contents):
|
|
|
|
return "Uploaded data contained a rom file, which is likely to contain copyrighted material. " \
|
|
|
|
"Your file was deleted."
|
|
|
|
|
2022-11-20 19:39:52 +00:00
|
|
|
# AP Container
|
|
|
|
elif handler:
|
2021-09-17 23:02:26 +00:00
|
|
|
data = zfile.open(file, "r").read()
|
2022-11-20 19:39:52 +00:00
|
|
|
patch = handler(BytesIO(data))
|
|
|
|
patch.read()
|
|
|
|
files[patch.player] = data
|
2022-07-20 10:48:14 +00:00
|
|
|
|
2022-11-20 19:39:52 +00:00
|
|
|
# Spoiler
|
2021-09-17 23:02:26 +00:00
|
|
|
elif file.filename.endswith(".txt"):
|
|
|
|
spoiler = zfile.open(file, "r").read().decode("utf-8-sig")
|
2022-03-18 03:53:09 +00:00
|
|
|
|
2022-11-20 19:39:52 +00:00
|
|
|
# Multi-data
|
2021-09-17 23:02:26 +00:00
|
|
|
elif file.filename.endswith(".archipelago"):
|
|
|
|
try:
|
|
|
|
multidata = zfile.open(file).read()
|
|
|
|
except:
|
|
|
|
flash("Could not load multidata. File may be corrupted or incompatible.")
|
2021-11-22 16:57:23 +00:00
|
|
|
multidata = None
|
|
|
|
|
2022-11-20 19:39:52 +00:00
|
|
|
# Minecraft
|
|
|
|
elif file.filename.endswith(".apmc"):
|
|
|
|
data = zfile.open(file, "r").read()
|
|
|
|
metadata = json.loads(base64.b64decode(data).decode("utf-8"))
|
|
|
|
files[metadata["player_id"]] = data
|
|
|
|
|
|
|
|
# Factorio
|
|
|
|
elif file.filename.endswith(".zip"):
|
|
|
|
_, _, slot_id, *_ = file.filename.split('_')[0].split('-', 3)
|
|
|
|
data = zfile.open(file, "r").read()
|
|
|
|
files[int(slot_id[1:])] = data
|
|
|
|
|
|
|
|
# All other files using the standard MultiWorld.get_out_file_name_base method
|
|
|
|
else:
|
|
|
|
_, _, slot_id, *_ = file.filename.split('.')[0].split('_', 3)
|
|
|
|
data = zfile.open(file, "r").read()
|
|
|
|
files[int(slot_id[1:])] = data
|
|
|
|
|
|
|
|
# Load multi data.
|
2021-09-17 23:02:26 +00:00
|
|
|
if multidata:
|
2022-01-01 16:18:48 +00:00
|
|
|
decompressed_multidata = MultiServer.Context.decompress(multidata)
|
2023-03-20 16:01:08 +00:00
|
|
|
recompress = False
|
|
|
|
|
|
|
|
if "datapackage" in decompressed_multidata:
|
|
|
|
# strip datapackage from multidata, leaving only the checksums
|
|
|
|
game_data_packages: typing.List[GameDataPackage] = []
|
|
|
|
for game, game_data in decompressed_multidata["datapackage"].items():
|
|
|
|
if game_data.get("checksum"):
|
|
|
|
game_data_package = GameDataPackage(checksum=game_data["checksum"],
|
|
|
|
data=pickle.dumps(game_data))
|
|
|
|
decompressed_multidata["datapackage"][game] = {
|
|
|
|
"version": game_data.get("version", 0),
|
|
|
|
"checksum": game_data["checksum"]
|
|
|
|
}
|
|
|
|
recompress = True
|
|
|
|
try:
|
|
|
|
commit() # commit game data package
|
|
|
|
game_data_packages.append(game_data_package)
|
|
|
|
except TransactionIntegrityError:
|
|
|
|
del game_data_package
|
|
|
|
rollback()
|
|
|
|
|
2022-02-23 18:16:45 +00:00
|
|
|
if "slot_info" in decompressed_multidata:
|
2022-11-20 19:39:52 +00:00
|
|
|
for slot, slot_info in decompressed_multidata["slot_info"].items():
|
|
|
|
# Ignore Player Groups (e.g. item links)
|
|
|
|
if slot_info.type == SlotType.group:
|
|
|
|
continue
|
|
|
|
slots.add(Slot(data=files.get(slot, None),
|
|
|
|
player_name=slot_info.name,
|
|
|
|
player_id=slot,
|
|
|
|
game=slot_info.game))
|
2022-02-23 18:16:45 +00:00
|
|
|
|
|
|
|
flush() # commit slots
|
2021-12-23 01:15:56 +00:00
|
|
|
|
2023-03-20 16:01:08 +00:00
|
|
|
if recompress:
|
|
|
|
multidata = multidata[0:1] + zlib.compress(pickle.dumps(decompressed_multidata), 9)
|
|
|
|
|
2021-09-17 23:02:26 +00:00
|
|
|
seed = Seed(multidata=multidata, spoiler=spoiler, slots=slots, owner=owner, meta=json.dumps(meta),
|
|
|
|
id=sid if sid else uuid.uuid4())
|
|
|
|
flush() # create seed
|
|
|
|
for slot in slots:
|
|
|
|
slot.seed = seed
|
|
|
|
return seed
|
|
|
|
else:
|
|
|
|
flash("No multidata was found in the zip file, which is required.")
|
|
|
|
|
|
|
|
|
2020-07-04 21:50:18 +00:00
|
|
|
@app.route('/uploads', methods=['GET', 'POST'])
|
|
|
|
def uploads():
|
2020-06-26 17:29:33 +00:00
|
|
|
if request.method == 'POST':
|
|
|
|
# check if the post request has the file part
|
|
|
|
if 'file' not in request.files:
|
|
|
|
flash('No file part')
|
|
|
|
else:
|
|
|
|
file = request.files['file']
|
|
|
|
# if user does not select file, browser also
|
|
|
|
# submit an empty part without filename
|
|
|
|
if file.filename == '':
|
|
|
|
flash('No selected file')
|
|
|
|
elif file and allowed_file(file.filename):
|
2022-01-06 05:09:15 +00:00
|
|
|
if zipfile.is_zipfile(file):
|
2020-06-26 17:29:33 +00:00
|
|
|
with zipfile.ZipFile(file, 'r') as zfile:
|
2022-01-18 07:23:38 +00:00
|
|
|
try:
|
|
|
|
res = upload_zip_to_db(zfile)
|
|
|
|
except VersionException:
|
|
|
|
flash(f"Could not load multidata. Wrong Version detected.")
|
|
|
|
else:
|
|
|
|
if type(res) == str:
|
|
|
|
return res
|
|
|
|
elif res:
|
|
|
|
return redirect(url_for("view_seed", seed=res.id))
|
2020-06-26 17:29:33 +00:00
|
|
|
else:
|
2022-01-08 20:21:29 +00:00
|
|
|
file.seek(0) # offset from is_zipfile check
|
2022-01-01 16:18:48 +00:00
|
|
|
# noinspection PyBroadException
|
2020-06-26 17:29:33 +00:00
|
|
|
try:
|
2021-04-04 01:18:19 +00:00
|
|
|
multidata = file.read()
|
2022-01-01 16:18:48 +00:00
|
|
|
MultiServer.Context.decompress(multidata)
|
2022-01-08 20:21:29 +00:00
|
|
|
except Exception as e:
|
|
|
|
flash(f"Could not load multidata. File may be corrupted or incompatible. ({e})")
|
2020-06-26 17:29:33 +00:00
|
|
|
else:
|
2020-06-27 11:52:03 +00:00
|
|
|
seed = Seed(multidata=multidata, owner=session["_id"])
|
2021-05-14 13:25:57 +00:00
|
|
|
flush() # place into DB and generate ids
|
2021-11-25 19:48:58 +00:00
|
|
|
return redirect(url_for("view_seed", seed=seed.id))
|
2020-06-26 17:29:33 +00:00
|
|
|
else:
|
2021-10-16 18:11:26 +00:00
|
|
|
flash("Not recognized file format. Awaiting a .archipelago file or .zip containing one.")
|
2022-01-31 01:06:03 +00:00
|
|
|
return render_template("hostGame.html", version=__version__)
|
2020-12-04 23:22:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.route('/user-content', methods=['GET'])
|
|
|
|
def user_content():
|
2020-06-26 17:29:33 +00:00
|
|
|
rooms = select(room for room in Room if room.owner == session["_id"])
|
2020-12-04 22:25:49 +00:00
|
|
|
seeds = select(seed for seed in Seed if seed.owner == session["_id"])
|
2020-12-04 23:22:12 +00:00
|
|
|
return render_template("userContent.html", rooms=rooms, seeds=seeds)
|
2020-07-27 02:05:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
def allowed_file(filename):
|
2021-01-03 13:32:32 +00:00
|
|
|
return filename.endswith(('.archipelago', ".zip"))
|