| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | import datetime | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2023-02-17 19:16:37 +01:00
										 |  |  | from typing import List, Dict, Union | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import jinja2.exceptions | 
					
						
							|  |  |  | from flask import request, redirect, url_for, render_template, Response, session, abort, send_from_directory | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | from pony.orm import count, commit, db_session | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | from worlds.AutoWorld import AutoWorldRegister | 
					
						
							|  |  |  | from . import app, cache | 
					
						
							| 
									
										
										
										
											2022-10-17 01:08:31 +02:00
										 |  |  | from .models import Seed, Room, Command, UUID, uuid4 | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_world_theme(game_name: str): | 
					
						
							|  |  |  |     if game_name in AutoWorldRegister.world_types: | 
					
						
							|  |  |  |         return AutoWorldRegister.world_types[game_name].web.theme | 
					
						
							|  |  |  |     return 'grass' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.before_request | 
					
						
							|  |  |  | def register_session(): | 
					
						
							|  |  |  |     session.permanent = True  # technically 31 days after the last visit | 
					
						
							|  |  |  |     if not session.get("_id", None): | 
					
						
							|  |  |  |         session["_id"] = uuid4()  # uniquely identify each session without needing a login | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.errorhandler(404) | 
					
						
							|  |  |  | @app.errorhandler(jinja2.exceptions.TemplateNotFound) | 
					
						
							|  |  |  | def page_not_found(err): | 
					
						
							|  |  |  |     return render_template('404.html'), 404 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Start Playing Page | 
					
						
							|  |  |  | @app.route('/start-playing') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def start_playing(): | 
					
						
							|  |  |  |     return render_template(f"startPlaying.html") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  | # TODO for back compat. remove around 0.4.5 | 
					
						
							|  |  |  | @app.route("/weighted-settings") | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def weighted_settings(): | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |     return redirect("weighted-options", 301) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route("/weighted-options") | 
					
						
							|  |  |  | @cache.cached() | 
					
						
							|  |  |  | def weighted_options(): | 
					
						
							|  |  |  |     return render_template("weighted-options.html") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Player options pages | 
					
						
							|  |  |  | @app.route("/games/<string:game>/player-options") | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  | def player_options(game: str): | 
					
						
							|  |  |  |     return render_template("player-options.html", game=game, theme=get_world_theme(game)) | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Game Info Pages | 
					
						
							|  |  |  | @app.route('/games/<string:game>/info/<string:lang>') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def game_info(game, lang): | 
					
						
							|  |  |  |     return render_template('gameInfo.html', game=game, lang=lang, theme=get_world_theme(game)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # List of supported games | 
					
						
							|  |  |  | @app.route('/games') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def games(): | 
					
						
							|  |  |  |     worlds = {} | 
					
						
							|  |  |  |     for game, world in AutoWorldRegister.world_types.items(): | 
					
						
							|  |  |  |         if not world.hidden: | 
					
						
							|  |  |  |             worlds[game] = world | 
					
						
							|  |  |  |     return render_template("supportedGames.html", worlds=worlds) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/tutorial/<string:game>/<string:file>/<string:lang>') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def tutorial(game, file, lang): | 
					
						
							|  |  |  |     return render_template("tutorial.html", game=game, file=file, lang=lang, theme=get_world_theme(game)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/tutorial/') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def tutorial_landing(): | 
					
						
							|  |  |  |     return render_template("tutorialLanding.html") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/faq/<string:lang>/') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def faq(lang): | 
					
						
							|  |  |  |     return render_template("faq.html", lang=lang) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/glossary/<string:lang>/') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def terms(lang): | 
					
						
							|  |  |  |     return render_template("glossary.html", lang=lang) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/seed/<suuid:seed>') | 
					
						
							|  |  |  | def view_seed(seed: UUID): | 
					
						
							|  |  |  |     seed = Seed.get(id=seed) | 
					
						
							|  |  |  |     if not seed: | 
					
						
							|  |  |  |         abort(404) | 
					
						
							|  |  |  |     return render_template("viewSeed.html", seed=seed, slot_count=count(seed.slots)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/new_room/<suuid:seed>') | 
					
						
							|  |  |  | def new_room(seed: UUID): | 
					
						
							|  |  |  |     seed = Seed.get(id=seed) | 
					
						
							|  |  |  |     if not seed: | 
					
						
							|  |  |  |         abort(404) | 
					
						
							|  |  |  |     room = Room(seed=seed, owner=session["_id"], tracker=uuid4()) | 
					
						
							|  |  |  |     commit() | 
					
						
							|  |  |  |     return redirect(url_for("host_room", room=room.id)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _read_log(path: str): | 
					
						
							|  |  |  |     if os.path.exists(path): | 
					
						
							|  |  |  |         with open(path, encoding="utf-8-sig") as log: | 
					
						
							|  |  |  |             yield from log | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         yield f"Logfile {path} does not exist. " \ | 
					
						
							|  |  |  |               f"Likely a crash during spinup of multiworld instance or it is still spinning up." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/log/<suuid:room>') | 
					
						
							|  |  |  | def display_log(room: UUID): | 
					
						
							|  |  |  |     room = Room.get(id=room) | 
					
						
							|  |  |  |     if room is None: | 
					
						
							|  |  |  |         return abort(404) | 
					
						
							|  |  |  |     if room.owner == session["_id"]: | 
					
						
							| 
									
										
										
										
											2023-04-21 15:12:43 +02:00
										 |  |  |         file_path = os.path.join("logs", str(room.id) + ".txt") | 
					
						
							|  |  |  |         if os.path.exists(file_path): | 
					
						
							|  |  |  |             return Response(_read_log(file_path), mimetype="text/plain;charset=UTF-8") | 
					
						
							|  |  |  |         return "Log File does not exist." | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     return "Access Denied", 403 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/room/<suuid:room>', methods=['GET', 'POST']) | 
					
						
							|  |  |  | def host_room(room: UUID): | 
					
						
							| 
									
										
										
										
											2022-08-12 04:55:40 +02:00
										 |  |  |     room: Room = Room.get(id=room) | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     if room is None: | 
					
						
							|  |  |  |         return abort(404) | 
					
						
							|  |  |  |     if request.method == "POST": | 
					
						
							|  |  |  |         if room.owner == session["_id"]: | 
					
						
							|  |  |  |             cmd = request.form["cmd"] | 
					
						
							|  |  |  |             if cmd: | 
					
						
							|  |  |  |                 Command(room=room, commandtext=cmd) | 
					
						
							|  |  |  |                 commit() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 04:55:40 +02:00
										 |  |  |     now = datetime.datetime.utcnow() | 
					
						
							|  |  |  |     # indicate that the page should reload to get the assigned port | 
					
						
							|  |  |  |     should_refresh = not room.last_port and now - room.creation_time < datetime.timedelta(seconds=3) | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     with db_session: | 
					
						
							| 
									
										
										
										
											2022-08-12 04:55:40 +02:00
										 |  |  |         room.last_activity = now  # will trigger a spinup, if it's not already running | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 04:55:40 +02:00
										 |  |  |     return render_template("hostRoom.html", room=room, should_refresh=should_refresh) | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/favicon.ico') | 
					
						
							|  |  |  | def favicon(): | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  |     return send_from_directory(os.path.join(app.root_path, "static", "static"), | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |                                'favicon.ico', mimetype='image/vnd.microsoft.icon') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/discord') | 
					
						
							|  |  |  | def discord(): | 
					
						
							| 
									
										
										
										
											2022-09-29 23:15:12 +02:00
										 |  |  |     return redirect("https://discord.gg/8Z65BR2") | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/datapackage') | 
					
						
							|  |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-11 00:58:08 +02:00
										 |  |  | def get_datapackage(): | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     """A pretty print version of /api/datapackage""" | 
					
						
							|  |  |  |     from worlds import network_data_package | 
					
						
							|  |  |  |     import json | 
					
						
							|  |  |  |     return Response(json.dumps(network_data_package, indent=4), mimetype="text/plain") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @app.route('/index') | 
					
						
							|  |  |  | @app.route('/sitemap') | 
					
						
							| 
									
										
										
										
											2023-10-02 20:06:56 +02:00
										 |  |  | @cache.cached() | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  | def get_sitemap(): | 
					
						
							| 
									
										
										
										
											2023-02-17 19:16:37 +01:00
										 |  |  |     available_games: List[Dict[str, Union[str, bool]]] = [] | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     for game, world in AutoWorldRegister.world_types.items(): | 
					
						
							|  |  |  |         if not world.hidden: | 
					
						
							| 
									
										
										
										
											2023-10-23 19:20:08 -05:00
										 |  |  |             has_settings: bool = isinstance(world.web.options_page, bool) and world.web.options_page | 
					
						
							| 
									
										
										
										
											2023-02-17 19:16:37 +01:00
										 |  |  |             available_games.append({ 'title': game, 'has_settings': has_settings }) | 
					
						
							| 
									
										
										
										
											2022-08-07 18:28:50 +02:00
										 |  |  |     return render_template("siteMap.html", games=available_games) |