| 
									
										
										
										
											2021-05-16 01:16:51 +02:00
										 |  |  | import base64 | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | import pickle | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import typing | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  | import uuid | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import zipfile | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | import zlib | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | from io import BytesIO | 
					
						
							| 
									
										
										
										
											2022-12-06 00:40:51 +01:00
										 |  |  | from flask import request, flash, redirect, url_for, session, render_template, Markup | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | from pony.orm import commit, flush, select, rollback | 
					
						
							|  |  |  | from pony.orm.core import TransactionIntegrityError | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | import MultiServer | 
					
						
							|  |  |  | from NetUtils import NetworkSlot, SlotType | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  | from Utils import VersionException, __version__ | 
					
						
							|  |  |  | from worlds.Files import AutoPatchRegister | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | from . import app | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | from .models import Seed, Room, Slot, GameDataPackage | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  | banned_zip_contents = (".sfc", ".z64", ".n64", ".sms", ".gb") | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 15:24:03 -07:00
										 |  |  | def process_multidata(compressed_multidata, files={}): | 
					
						
							|  |  |  |     decompressed_multidata = MultiServer.Context.decompress(compressed_multidata) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     slots: typing.Set[Slot] = set() | 
					
						
							|  |  |  |     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"] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     commit()  # commit game data package | 
					
						
							|  |  |  |                     game_data_packages.append(game_data_package) | 
					
						
							|  |  |  |                 except TransactionIntegrityError: | 
					
						
							|  |  |  |                     del game_data_package | 
					
						
							|  |  |  |                     rollback() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if "slot_info" in decompressed_multidata: | 
					
						
							|  |  |  |         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)) | 
					
						
							|  |  |  |         flush()  # commit slots | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     compressed_multidata = compressed_multidata[0:1] + zlib.compress(pickle.dumps(decompressed_multidata), 9) | 
					
						
							|  |  |  |     return slots, compressed_multidata | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02: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-06 00:40:51 +01: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 | 
					
						
							| 
									
										
										
										
											2023-04-06 15:24:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  |     spoiler = "" | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  |     files = {} | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  |     multidata = None | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Load files. | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  |     for file in infolist: | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  |         handler = AutoPatchRegister.get_handler(file.filename) | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02: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 13:39:52 -06:00
										 |  |  |         # AP Container | 
					
						
							|  |  |  |         elif handler: | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  |             data = zfile.open(file, "r").read() | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  |             patch = handler(BytesIO(data)) | 
					
						
							|  |  |  |             patch.read() | 
					
						
							|  |  |  |             files[patch.player] = data | 
					
						
							| 
									
										
										
										
											2022-07-20 12:48:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  |         # Spoiler | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02:00
										 |  |  |         elif file.filename.endswith(".txt"): | 
					
						
							|  |  |  |             spoiler = zfile.open(file, "r").read().decode("utf-8-sig") | 
					
						
							| 
									
										
										
										
											2022-03-18 04:53:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06:00
										 |  |  |         # Multi-data | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02: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 17:57:23 +01:00
										 |  |  |                 multidata = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-20 13:39:52 -06: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-18 01:02:26 +02:00
										 |  |  |     if multidata: | 
					
						
							| 
									
										
										
										
											2023-04-06 15:24:03 -07:00
										 |  |  |         slots, multidata = process_multidata(multidata, files) | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-18 01:02:26 +02: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 23:50:18 +02:00
										 |  |  | @app.route('/uploads', methods=['GET', 'POST']) | 
					
						
							|  |  |  | def uploads(): | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02: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 06:09:15 +01:00
										 |  |  |                 if zipfile.is_zipfile(file): | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  |                     with zipfile.ZipFile(file, 'r') as zfile: | 
					
						
							| 
									
										
										
										
											2022-01-18 08:23:38 +01: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 19:29:33 +02:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2022-01-08 21:21:29 +01:00
										 |  |  |                     file.seek(0)  # offset from is_zipfile check | 
					
						
							| 
									
										
										
										
											2022-01-01 17:18:48 +01:00
										 |  |  |                     # noinspection PyBroadException | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  |                     try: | 
					
						
							| 
									
										
										
										
											2021-04-04 03:18:19 +02:00
										 |  |  |                         multidata = file.read() | 
					
						
							| 
									
										
										
										
											2023-04-06 15:24:03 -07:00
										 |  |  |                         slots, multidata = process_multidata(multidata) | 
					
						
							| 
									
										
										
										
											2022-01-08 21:21:29 +01:00
										 |  |  |                     except Exception as e: | 
					
						
							|  |  |  |                         flash(f"Could not load multidata. File may be corrupted or incompatible. ({e})") | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2023-04-06 15:24:03 -07:00
										 |  |  |                         seed = Seed(multidata=multidata, slots=slots, owner=session["_id"]) | 
					
						
							| 
									
										
										
										
											2021-05-14 15:25:57 +02:00
										 |  |  |                         flush()  # place into DB and generate ids | 
					
						
							| 
									
										
										
										
											2021-11-25 20:48:58 +01:00
										 |  |  |                         return redirect(url_for("view_seed", seed=seed.id)) | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-10-16 20:11:26 +02:00
										 |  |  |                 flash("Not recognized file format. Awaiting a .archipelago file or .zip containing one.") | 
					
						
							| 
									
										
										
										
											2022-01-30 20:06:03 -05:00
										 |  |  |     return render_template("hostGame.html", version=__version__) | 
					
						
							| 
									
										
										
										
											2020-12-04 18:22:12 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/user-content', methods=['GET']) | 
					
						
							|  |  |  | def user_content(): | 
					
						
							| 
									
										
										
										
											2020-06-26 19:29:33 +02:00
										 |  |  |     rooms = select(room for room in Room if room.owner == session["_id"]) | 
					
						
							| 
									
										
										
										
											2020-12-04 23:25:49 +01:00
										 |  |  |     seeds = select(seed for seed in Seed if seed.owner == session["_id"]) | 
					
						
							| 
									
										
										
										
											2020-12-04 18:22:12 -05:00
										 |  |  |     return render_template("userContent.html", rooms=rooms, seeds=seeds) | 
					
						
							| 
									
										
										
										
											2020-07-27 04:05:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def allowed_file(filename): | 
					
						
							| 
									
										
										
										
											2021-01-03 14:32:32 +01:00
										 |  |  |     return filename.endswith(('.archipelago', ".zip")) |