277 lines
12 KiB
Python
277 lines
12 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify
|
|
from app.models.gameservers import GameServer
|
|
from app.models.place_ordered_datastore import PlaceOrderedDatastore
|
|
from app.models.place_datastore import PlaceDatastore
|
|
from app.models.asset import Asset
|
|
from app.models.universe import Universe
|
|
from app.models.place import Place
|
|
from app.enums.AssetType import AssetType
|
|
from app.extensions import get_remote_address, db, redis_controller, csrf
|
|
from functools import wraps
|
|
|
|
DataStoreRoute = Blueprint('datastore', __name__)
|
|
|
|
def GameServerRequired(f):
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
RequestIP = get_remote_address()
|
|
if RequestIP is None:
|
|
return jsonify({"success": False, "message": "Unauthorized"}),401
|
|
|
|
server : GameServer = GameServer.query.filter_by(serverIP=RequestIP).first()
|
|
if server is None:
|
|
return jsonify({"success": False, "message": "Unauthorized"}),401
|
|
|
|
ServerAccessKey = request.headers.get("AccessKey", default = None, type = str)
|
|
if ServerAccessKey is None or ServerAccessKey != server.accessKey:
|
|
return jsonify({"success": False, "message": "Unauthorized"}),401
|
|
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
|
|
@DataStoreRoute.route("/persistence/getSortedValues", methods=["POST"])
|
|
@csrf.exempt
|
|
@GameServerRequired
|
|
def getSortedValues():
|
|
placeId : int = request.args.get("placeId", default=None, type=int)
|
|
dataType : str = request.args.get("type", default=None, type=str)
|
|
scope : str = request.args.get("scope", default="global", type=str)
|
|
pageSize : int = request.args.get("pageSize", default=50, type=int)
|
|
exclusiveStartKey : str = request.args.get("exclusiveStartKey", default=None, type=int) # I have no idea how Roblox uses this, but I am going to use it as a page number with pagination
|
|
key : str = request.args.get("key", default=None, type=str)
|
|
ascending : bool = request.args.get("ascending", default="False", type=str) == "True"
|
|
inclusiveMinValue : int = request.args.get("inclusiveMinValue", default=None, type=int)
|
|
exclusiveMaxValue : int = request.args.get("inclusiveMaxValue", default=None, type=int)
|
|
|
|
PlaceObj : Place = Place.query.filter_by(placeid = placeId).first()
|
|
if PlaceObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
UniverseObj : Universe = Universe.query.filter_by(id = PlaceObj.parent_universe_id).first()
|
|
if UniverseObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
|
|
if pageSize == 0 or pageSize > 100:
|
|
return jsonify({"data":[], "message": "Page size is too large"}), 200
|
|
if dataType != "sorted":
|
|
return jsonify({"data":[], "message": "Invalid data type"}), 200
|
|
|
|
if exclusiveStartKey is None:
|
|
exclusiveStartKey = 1
|
|
else:
|
|
if exclusiveStartKey < 1:
|
|
return jsonify({"data":[], "message": "Invalid exclusive start key"}), 200
|
|
|
|
DataStoreObj = PlaceOrderedDatastore.query.filter_by(
|
|
universe_id = UniverseObj.id,
|
|
scope = scope,
|
|
key = key
|
|
)
|
|
if inclusiveMinValue is not None:
|
|
DataStoreObj = DataStoreObj.filter(PlaceOrderedDatastore.value >= inclusiveMinValue)
|
|
if exclusiveMaxValue is not None:
|
|
DataStoreObj = DataStoreObj.filter(PlaceOrderedDatastore.value < exclusiveMaxValue)
|
|
DataStoreObj : list[PlaceOrderedDatastore] = DataStoreObj.order_by(PlaceOrderedDatastore.value.asc() if ascending else PlaceOrderedDatastore.value.desc()).paginate( page=exclusiveStartKey, per_page=pageSize, error_out=False )
|
|
if DataStoreObj is None:
|
|
return jsonify({"Entries": [], "ExclusiveStartKey": None}), 200
|
|
|
|
AllEntries = []
|
|
for entry in DataStoreObj.items:
|
|
AllEntries.append({
|
|
"Target": entry.name,
|
|
"Value": entry.value
|
|
})
|
|
|
|
return jsonify({"data":{
|
|
"Entries": AllEntries,
|
|
"ExclusiveStartKey": str(DataStoreObj.next_num) if DataStoreObj.has_next else None
|
|
}}) # TODO: Implement
|
|
|
|
@DataStoreRoute.route("/persistence/set", methods=["POST"])
|
|
@csrf.exempt
|
|
@GameServerRequired
|
|
def setKey():
|
|
placeId : int = request.args.get("placeId", default=None, type=int)
|
|
dataType : str = request.args.get("type", default=None, type=str)
|
|
scope : str = request.args.get("scope", default="global", type=str)
|
|
key : str = request.args.get("key", default=None, type=str)
|
|
target : str = request.args.get("target", default=None, type=str)
|
|
valueLength : int = request.args.get("valueLength", default=None, type=int)
|
|
|
|
value : str = request.form.get("value", default=None, type=str)
|
|
|
|
if valueLength is not None and not valueLength < 1024 * 1024 * 1: # 1MB
|
|
return jsonify({"success": False, "message": "Value too large"}),400
|
|
|
|
PlaceObj : Place = Place.query.filter_by(placeid = placeId).first()
|
|
if PlaceObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
UniverseObj : Universe = Universe.query.filter_by(id = PlaceObj.parent_universe_id).first()
|
|
if UniverseObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
|
|
if dataType == "standard":
|
|
DataStoreObj : PlaceDatastore = PlaceDatastore.query.filter_by(
|
|
universe_id = UniverseObj.id,
|
|
scope = scope,
|
|
key = key,
|
|
name = target
|
|
).order_by(PlaceDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is None:
|
|
DataStoreObj : PlaceDatastore = PlaceDatastore(
|
|
placeid = placeId,
|
|
universe_id = UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target,
|
|
value=value
|
|
)
|
|
db.session.add(DataStoreObj)
|
|
else:
|
|
DataStoreObj.value = value
|
|
db.session.commit()
|
|
elif dataType == "sorted":
|
|
try:
|
|
value : int = int(value)
|
|
except:
|
|
return jsonify({"success": False, "message": "Value is not an integer"}), 400
|
|
|
|
DataStoreObj : PlaceOrderedDatastore = PlaceOrderedDatastore.query.filter_by(
|
|
universe_id = UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target
|
|
).order_by(PlaceOrderedDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is None:
|
|
DataStoreObj : PlaceOrderedDatastore = PlaceOrderedDatastore(
|
|
placeid = placeId,
|
|
universe_id = UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target,
|
|
value=value
|
|
)
|
|
db.session.add(DataStoreObj)
|
|
else:
|
|
DataStoreObj.value = value
|
|
db.session.commit()
|
|
|
|
return jsonify({"data":value}) # TODO: Implement
|
|
|
|
@DataStoreRoute.route("/persistence/getV2", methods=["POST"])
|
|
@DataStoreRoute.route("/persistence/getv2", methods=["POST"])
|
|
@csrf.exempt
|
|
@GameServerRequired
|
|
def getv2():
|
|
placeId : int = request.args.get("placeId", default=None, type=int)
|
|
dataType : str = request.args.get("type", default=None, type=str)
|
|
scope : str = request.args.get("scope", default="global", type=str)
|
|
|
|
PlaceObj : Place = Place.query.filter_by(placeid = placeId).first()
|
|
if PlaceObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
UniverseObj : Universe = Universe.query.filter_by(id = PlaceObj.parent_universe_id).first()
|
|
if UniverseObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
|
|
dataBeingRequested = []
|
|
|
|
StartingCount = 0
|
|
while True:
|
|
ReqScope : str = request.form.get(f"qkeys[{str(StartingCount)}].scope", default=None, type=str)
|
|
if ReqScope is None:
|
|
break
|
|
Target : str = request.form.get(f"qkeys[{str(StartingCount)}].target", default=None, type=str)
|
|
DataStoreName : str = request.form.get(f"qkeys[{str(StartingCount)}].key", default=None, type=str)
|
|
if DataStoreName is None or Target is None or ReqScope is None:
|
|
break
|
|
dataBeingRequested.append({"Scope": ReqScope, "Target": Target, "Key": DataStoreName})
|
|
StartingCount += 1
|
|
if len(dataBeingRequested) == 0:
|
|
return jsonify({"data":[], "message": "No data being requested"}), 200
|
|
|
|
ReturnData = []
|
|
if dataType == 'standard':
|
|
for targetReq in dataBeingRequested:
|
|
DataStoreObj : PlaceDatastore = PlaceDatastore.query.filter_by(universe_id = UniverseObj.id, scope=targetReq["Scope"], key=targetReq["Key"], name=targetReq["Target"]).order_by(PlaceDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is not None:
|
|
ReturnData.append({
|
|
"Value": DataStoreObj.value,
|
|
"Scope": DataStoreObj.scope,
|
|
"Key": DataStoreObj.key,
|
|
"Target": DataStoreObj.name
|
|
})
|
|
elif dataType == 'sorted':
|
|
for targetReq in dataBeingRequested:
|
|
DataStoreObj : PlaceOrderedDatastore = PlaceOrderedDatastore.query.filter_by(universe_id = UniverseObj.id, scope=targetReq["Scope"], key=targetReq["Key"], name=targetReq["Target"]).order_by(PlaceOrderedDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is not None:
|
|
ReturnData.append({
|
|
"Value": str(DataStoreObj.value),
|
|
"Scope": DataStoreObj.scope,
|
|
"Key": DataStoreObj.key,
|
|
"Target": DataStoreObj.name
|
|
})
|
|
|
|
return jsonify({"data":ReturnData})
|
|
|
|
@DataStoreRoute.route("/persistence/increment", methods=["POST"])
|
|
@csrf.exempt
|
|
@GameServerRequired
|
|
def increment():
|
|
placeId : int = request.args.get("placeId", default=None, type=int)
|
|
key : str = request.args.get("key", default=None, type=str)
|
|
dataType : str = request.args.get("type", default=None, type=str)
|
|
scope : str = request.args.get("scope", default="global", type=str)
|
|
target : str = request.args.get("target", default=None, type=str)
|
|
value : int = request.args.get("value", default=None, type=int)
|
|
|
|
PlaceObj : Place = Place.query.filter_by(placeid = placeId).first()
|
|
if PlaceObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
UniverseObj : Universe = Universe.query.filter_by(id = PlaceObj.parent_universe_id).first()
|
|
if UniverseObj is None:
|
|
return jsonify({"data":[], "message": "Place does not exist"}), 200
|
|
|
|
if dataType == "standard":
|
|
DataStoreObj : PlaceDatastore = PlaceDatastore.query.filter_by(
|
|
universe_id=UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target
|
|
).order_by(PlaceDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is None:
|
|
DataStoreObj : PlaceDatastore = PlaceDatastore(
|
|
placeid = placeId,
|
|
universe_id=UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target,
|
|
value=str(value)
|
|
)
|
|
db.session.add(DataStoreObj)
|
|
else:
|
|
try:
|
|
DataStoreObj.value = str(int(DataStoreObj.value) + value)
|
|
except:
|
|
return jsonify({"data":[], "message": "Value is not an integer"}), 200
|
|
db.session.commit()
|
|
elif dataType == "sorted":
|
|
DataStoreObj : PlaceOrderedDatastore = PlaceOrderedDatastore.query.filter_by(
|
|
universe_id=UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target
|
|
).order_by(PlaceOrderedDatastore.updated_at.desc()).first()
|
|
if DataStoreObj is None:
|
|
DataStoreObj : PlaceOrderedDatastore = PlaceOrderedDatastore(
|
|
placeid = placeId,
|
|
universe_id=UniverseObj.id,
|
|
scope=scope,
|
|
key=key,
|
|
name=target,
|
|
value=value
|
|
)
|
|
db.session.add(DataStoreObj)
|
|
else:
|
|
DataStoreObj.value += value # PlaceOrderedDatastore values should always be integers
|
|
|
|
return jsonify({"data":value}) |