mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Merge master into website-redesign
This commit is contained in:
@@ -47,6 +47,8 @@ app.config["PONY"] = {
|
||||
}
|
||||
app.config["MAX_ROLL"] = 20
|
||||
app.config["CACHE_TYPE"] = "simple"
|
||||
app.config["JSON_AS_ASCII"] = False
|
||||
|
||||
app.autoversion = True
|
||||
av = Autoversion(app)
|
||||
cache = Cache(app)
|
||||
@@ -145,4 +147,5 @@ def favicon():
|
||||
|
||||
|
||||
from WebHostLib.customserver import run_server_process
|
||||
from . import tracker, upload, landing, check, generate, downloads # to trigger app routing picking up on it
|
||||
from . import tracker, upload, landing, check, generate, downloads, api # to trigger app routing picking up on it
|
||||
app.register_blueprint(api.api_endpoints)
|
23
WebHostLib/api/__init__.py
Normal file
23
WebHostLib/api/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""API endpoints package."""
|
||||
from uuid import UUID
|
||||
|
||||
from flask import Blueprint, abort
|
||||
|
||||
from ..models import Room
|
||||
|
||||
api_endpoints = Blueprint('api', __name__, url_prefix="/api")
|
||||
|
||||
from . import generate
|
||||
|
||||
# unsorted/misc endpoints
|
||||
|
||||
@api_endpoints.route('/room_status/<suuid:room>')
|
||||
def room_info(room: UUID):
|
||||
room = Room.get(id=room)
|
||||
if room is None:
|
||||
return abort(404)
|
||||
return {"tracker": room.tracker,
|
||||
"players": room.seed.multidata["names"],
|
||||
"last_port": room.last_port,
|
||||
"last_activity": room.last_activity,
|
||||
"timeout": room.timeout}
|
73
WebHostLib/api/generate.py
Normal file
73
WebHostLib/api/generate.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import pickle
|
||||
from uuid import UUID
|
||||
|
||||
from . import api_endpoints
|
||||
from flask import request, session, url_for
|
||||
from pony.orm import commit
|
||||
|
||||
from WebHostLib import app, Generation, STATE_QUEUED, Seed, STATE_ERROR
|
||||
from WebHostLib.check import get_yaml_data, roll_options
|
||||
|
||||
|
||||
@api_endpoints.route('/generate', methods=['POST'])
|
||||
def generate_api():
|
||||
try:
|
||||
options = {}
|
||||
race = False
|
||||
|
||||
if 'file' in request.files:
|
||||
file = request.files['file']
|
||||
options = get_yaml_data(file)
|
||||
if type(options) == str:
|
||||
return {"text": options}, 400
|
||||
if "race" in request.form:
|
||||
race = bool(0 if request.form["race"] in {"false"} else int(request.form["race"]))
|
||||
|
||||
json_data = request.get_json()
|
||||
if json_data:
|
||||
if 'weights' in json_data:
|
||||
# example: options = {"player1weights" : {<weightsdata>}}
|
||||
options = json_data["weights"]
|
||||
if "race" in json_data:
|
||||
race = bool(0 if json_data["race"] in {"false"} else int(json_data["race"]))
|
||||
if not options:
|
||||
return {"text": "No options found. Expected file attachment or json weights."
|
||||
}, 400
|
||||
|
||||
if len(options) > app.config["MAX_ROLL"]:
|
||||
return {"text": "Max size of multiworld exceeded",
|
||||
"detail": app.config["MAX_ROLL"]}, 409
|
||||
|
||||
results, gen_options = roll_options(options)
|
||||
if any(type(result) == str for result in results.values()):
|
||||
return {"text": str(results),
|
||||
"detail": results}, 400
|
||||
else:
|
||||
gen = Generation(
|
||||
options=pickle.dumps({name: vars(options) for name, options in gen_options.items()}),
|
||||
# convert to json compatible
|
||||
meta=pickle.dumps({"race": race}), state=STATE_QUEUED,
|
||||
owner=session["_id"])
|
||||
commit()
|
||||
return {"text": f"Generation of seed {gen.id} started successfully.",
|
||||
"detail": gen.id,
|
||||
"encoded": app.url_map.converters["suuid"].to_url(None, gen.id),
|
||||
"wait_api_url": url_for("wait_seed_api", seed=gen.id, _external=True),
|
||||
"url": url_for("wait_seed", seed=gen.id, _external=True)}, 201
|
||||
except Exception as e:
|
||||
return {"text": "Uncaught Exception:" + str(e)}, 500
|
||||
|
||||
|
||||
@api_endpoints.route('/status/<suuid:seed>')
|
||||
def wait_seed_api(seed: UUID):
|
||||
seed_id = seed
|
||||
seed = Seed.get(id=seed_id)
|
||||
if seed:
|
||||
return {"text": "Generation done"}, 201
|
||||
generation = Generation.get(id=seed_id)
|
||||
|
||||
if not generation:
|
||||
return {"text": "Generation not found"}, 404
|
||||
elif generation.state == STATE_ERROR:
|
||||
return {"text": "Generation failed"}, 500
|
||||
return {"text": "Generation running"}, 202
|
@@ -47,7 +47,7 @@ class DBCommandProcessor(ServerCommandProcessor):
|
||||
|
||||
class WebHostContext(Context):
|
||||
def __init__(self):
|
||||
super(WebHostContext, self).__init__("", 0, "", 1, 40, True, "enabled", "enabled", 0, 2)
|
||||
super(WebHostContext, self).__init__("", 0, "", "", 1, 40, True, "enabled", "enabled", 0, 2)
|
||||
self.main_loop = asyncio.get_running_loop()
|
||||
self.video = {}
|
||||
self.tags = ["Berserker", "WebHost"]
|
||||
|
@@ -52,55 +52,6 @@ def generate(race=False):
|
||||
return render_template("generate.html", race=race)
|
||||
|
||||
|
||||
@app.route('/api/generate', methods=['POST'])
|
||||
def generate_api():
|
||||
try:
|
||||
options = {}
|
||||
race = False
|
||||
|
||||
if 'file' in request.files:
|
||||
file = request.files['file']
|
||||
options = get_yaml_data(file)
|
||||
if type(options) == str:
|
||||
return {"text": options}, 400
|
||||
if "race" in request.form:
|
||||
race = bool(0 if request.form["race"] in {"false"} else int(request.form["race"]))
|
||||
|
||||
json_data = request.get_json()
|
||||
if json_data:
|
||||
if 'weights' in json_data:
|
||||
# example: options = {"player1weights" : {<weightsdata>}}
|
||||
options = json_data["weights"]
|
||||
if "race" in json_data:
|
||||
race = bool(0 if json_data["race"] in {"false"} else int(json_data["race"]))
|
||||
if not options:
|
||||
return {"text": "No options found. Expected file attachment or json weights."
|
||||
}, 400
|
||||
|
||||
if len(options) > app.config["MAX_ROLL"]:
|
||||
return {"text": "Max size of multiworld exceeded",
|
||||
"detail": app.config["MAX_ROLL"]}, 409
|
||||
|
||||
results, gen_options = roll_options(options)
|
||||
if any(type(result) == str for result in results.values()):
|
||||
return {"text": str(results),
|
||||
"detail": results}, 400
|
||||
else:
|
||||
gen = Generation(
|
||||
options=pickle.dumps({name: vars(options) for name, options in gen_options.items()}),
|
||||
# convert to json compatible
|
||||
meta=pickle.dumps({"race": race}), state=STATE_QUEUED,
|
||||
owner=session["_id"])
|
||||
commit()
|
||||
return {"text": f"Generation of seed {gen.id} started successfully.",
|
||||
"detail": gen.id,
|
||||
"encoded": app.url_map.converters["suuid"].to_url(None, gen.id),
|
||||
"wait_api_url": url_for("wait_seed_api", seed=gen.id),
|
||||
"url": url_for("wait_seed", seed=gen.id)}, 201
|
||||
except Exception as e:
|
||||
return {"text": "Uncaught Exception:" + str(e)}, 500
|
||||
|
||||
|
||||
def gen_game(gen_options, race=False, owner=None, sid=None):
|
||||
try:
|
||||
target = tempfile.TemporaryDirectory()
|
||||
@@ -142,12 +93,13 @@ def gen_game(gen_options, race=False, owner=None, sid=None):
|
||||
ERmain(erargs, seed)
|
||||
|
||||
return upload_to_db(target.name, owner, sid, race)
|
||||
except BaseException:
|
||||
except BaseException as e:
|
||||
if sid:
|
||||
with db_session:
|
||||
gen = Generation.get(id=sid)
|
||||
if gen is not None:
|
||||
gen.state = STATE_ERROR
|
||||
gen.meta = (e.__class__.__name__ + ": "+ str(e)).encode()
|
||||
raise
|
||||
|
||||
|
||||
@@ -162,25 +114,11 @@ def wait_seed(seed: UUID):
|
||||
if not generation:
|
||||
return "Generation not found."
|
||||
elif generation.state == STATE_ERROR:
|
||||
return "Generation failed, please retry."
|
||||
import html
|
||||
return f"Generation failed, please retry. <br> {html.escape(generation.meta.decode())}"
|
||||
return render_template("waitSeed.html", seed_id=seed_id)
|
||||
|
||||
|
||||
@app.route('/api/status/<suuid:seed>')
|
||||
def wait_seed_api(seed: UUID):
|
||||
seed_id = seed
|
||||
seed = Seed.get(id=seed_id)
|
||||
if seed:
|
||||
return {"text": "Generation done"}, 201
|
||||
generation = Generation.get(id=seed_id)
|
||||
|
||||
if not generation:
|
||||
return {"text": "Generation not found"}, 404
|
||||
elif generation.state == STATE_ERROR:
|
||||
return {"text": "Generation failed"}, 500
|
||||
return {"text": "Generation running"}, 202
|
||||
|
||||
|
||||
def upload_to_db(folder, owner, sid, race:bool):
|
||||
patches = set()
|
||||
spoiler = ""
|
||||
|
@@ -50,5 +50,5 @@ class Generation(db.Entity):
|
||||
id = PrimaryKey(UUID, default=uuid4)
|
||||
owner = Required(UUID)
|
||||
options = Required(bytes, lazy=True) # these didn't work as JSON on mariaDB, so they're getting pickled now
|
||||
meta = Required(bytes, lazy=True)
|
||||
meta = Required(bytes, lazy=True) # if state is -1 (error) this will contain an utf-8 encoded error message
|
||||
state = Required(int, default=0, index=True)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
flask>=1.1.2
|
||||
pony>=0.7.13
|
||||
pony>=0.7.14
|
||||
waitress>=1.4.4
|
||||
flask-caching>=1.9.0
|
||||
Flask-Autoversion>=0.2.0
|
||||
Flask-Compress>=1.7.0
|
||||
Flask-Compress>=1.8.0
|
||||
Flask-Limiter>=1.4
|
||||
|
@@ -791,12 +791,6 @@
|
||||
"friendlyName": "Expert",
|
||||
"description": "Minimum upgrade availability (max: 8 hearts, green mail, master sword, fighter shield, no silvers unless swordless).",
|
||||
"defaultValue": 0
|
||||
},
|
||||
"crowd_control": {
|
||||
"keyString": "item_pool.crowd_control",
|
||||
"friendlyName": "Crowd Control",
|
||||
"description": "Configures the item pool for the crowd control extension. Do not use this unless you are using crowd control.",
|
||||
"defaultValue": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1387,6 +1381,26 @@
|
||||
"defaultValue": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"key_drop_shuffle": {
|
||||
"keyString": "key_drop_shuffle",
|
||||
"friendlyName": "Key Drop Shuffle",
|
||||
"description": "Allows the small/big keys dropped by enemies/pots to be shuffled into the item pool. This extends the number of checks from 216 to 249",
|
||||
"inputType": "range",
|
||||
"subOptions": {
|
||||
"on": {
|
||||
"keyString": "key_drop_shuffle.on",
|
||||
"friendlyName": "Enabled",
|
||||
"description": "Enables key drop shuffle",
|
||||
"defaultValue": 0
|
||||
},
|
||||
"off": {
|
||||
"keyString": "key_drop_shuffle.off",
|
||||
"friendlyName": "Disabled",
|
||||
"description": "Disables key drop shuffle",
|
||||
"defaultValue": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"romOptions": {
|
||||
|
@@ -165,7 +165,6 @@ item_pool:
|
||||
normal: 50 # Item availability remains unchanged from vanilla game
|
||||
hard: 0 # Reduced upgrade availability (max: 14 hearts, blue mail, tempered sword, fire shield, no silvers unless swordless)
|
||||
expert: 0 # Minimum upgrade availability (max: 8 hearts, green mail, master sword, fighter shield, no silvers unless swordless)
|
||||
crowd_control: 0 # Sets up the item pool for the crowd control extension. Do not use it without crowd control
|
||||
item_functionality:
|
||||
easy: 0 # Allow Hammer to damage ganon, Allow Hammer tablet collection, Allow swordless medallion use everywhere.
|
||||
normal: 50 # Vanilla item functionality
|
||||
@@ -305,6 +304,9 @@ intensity: # Only available if the host uses the doors branch, it is ignored oth
|
||||
2: 0 # And shuffles open edges and straight staircases
|
||||
3: 0 # And shuffles dungeon lobbies
|
||||
random: 0 # Picks one of those at random
|
||||
key_drop_shuffle: # Only available if the host uses the doors branch, it is ignored otherwise
|
||||
on: 0 # Enables the small keys dropped by enemies or under pots, and the big key dropped by the Ball & Chain guard to be shuffled into the pool. This extends the number of checks to 249.
|
||||
off: 50
|
||||
experimental: # Only available if the host uses the doors branch, it is ignored otherwise
|
||||
on: 0 # Enables experimental features. Currently, this is just the dungeon keys in chest counter.
|
||||
off: 50
|
||||
|
@@ -55,6 +55,7 @@ html{
|
||||
#player-settings #settings-wrapper #sprite-picker .sprite-img-wrapper{
|
||||
cursor: pointer;
|
||||
margin: 10px;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
/* Center tooltip text for sprite images */
|
||||
|
Reference in New Issue
Block a user