Format code with black, include wrapper script to handle that, update linter checks to accomodate, fix a few miscelaneous type errors.
This commit is contained in:
parent
d6cddebe88
commit
25e162042a
10
README.md
10
README.md
@ -116,6 +116,13 @@ bring your production DB up to sync with the code you are deploying. Run it like
|
||||
`./dbutils --help` to see all options. The config file that this works on is the same
|
||||
that is given to "api", "services" and "frontend".
|
||||
|
||||
## formatfiles
|
||||
|
||||
A simple wrapper frontend to black, the formatter used on this project. Running this will
|
||||
auto-format all of the python code that might need formatting, leaving the rest out. When
|
||||
submitting pull requests make sure to run this so that your code conforms to the style
|
||||
used by this project!
|
||||
|
||||
## frontend
|
||||
|
||||
Development version of a frontend server allowing for account and server administration
|
||||
@ -722,7 +729,8 @@ up your MySQL instance, see the `examples/` directory.
|
||||
Contributions are welcome! Before submitting a pull request, ensure that your code
|
||||
is type-hint clean by running `./verifytyping` and ensure that it hasn't broken basic
|
||||
libraries with `./verifylibs`. Make sure that it is also lint-clean with `./verifylint`.
|
||||
If you are changing code related to a particular game, it is nice to include a
|
||||
You should also make sure its formatted correctly by running `./formatfiles`.
|
||||
If you are changing code related to a particular game, it is required to include a
|
||||
verification in the form of a game traffic emulator, so that basic functionality can
|
||||
be verified. To ensure you haven't broken another game with your changes, its recommended
|
||||
to run the traffic generator against your code with various games. For convenience, you
|
||||
|
@ -6,22 +6,25 @@ from flask import Flask, abort, request, Response
|
||||
from functools import wraps
|
||||
|
||||
from bemani.api.exceptions import APIException
|
||||
from bemani.api.objects import RecordsObject, ProfileObject, StatisticsObject, CatalogObject
|
||||
from bemani.api.objects import (
|
||||
RecordsObject,
|
||||
ProfileObject,
|
||||
StatisticsObject,
|
||||
CatalogObject,
|
||||
)
|
||||
from bemani.api.types import g
|
||||
from bemani.common import GameConstants, APIConstants, VersionConstants
|
||||
from bemani.data import Config, Data
|
||||
|
||||
app = Flask(
|
||||
__name__
|
||||
)
|
||||
app = Flask(__name__)
|
||||
config = Config()
|
||||
|
||||
SUPPORTED_VERSIONS: List[str] = ['v1']
|
||||
SUPPORTED_VERSIONS: List[str] = ["v1"]
|
||||
|
||||
|
||||
def jsonify_response(data: Dict[str, Any], code: int=200) -> Response:
|
||||
def jsonify_response(data: Dict[str, Any], code: int = 200) -> Response:
|
||||
return Response(
|
||||
json.dumps(data).encode('utf8'),
|
||||
json.dumps(data).encode("utf8"),
|
||||
content_type="application/json; charset=utf-8",
|
||||
status=code,
|
||||
)
|
||||
@ -35,15 +38,15 @@ def before_request() -> None:
|
||||
g.data = Data(config)
|
||||
g.authorized = False
|
||||
|
||||
authkey = request.headers.get('Authorization')
|
||||
authkey = request.headers.get("Authorization")
|
||||
if authkey is not None:
|
||||
try:
|
||||
authtype, authtoken = authkey.split(' ', 1)
|
||||
authtype, authtoken = authkey.split(" ", 1)
|
||||
except ValueError:
|
||||
authtype = None
|
||||
authtoken = None
|
||||
|
||||
if authtype.lower() == 'token':
|
||||
if authtype.lower() == "token":
|
||||
g.authorized = g.data.local.api.validate_client(authtoken)
|
||||
|
||||
|
||||
@ -59,7 +62,7 @@ def after_request(response: Response) -> Response:
|
||||
|
||||
@app.teardown_request
|
||||
def teardown_request(exception: Any) -> None:
|
||||
data = getattr(g, 'data', None)
|
||||
data = getattr(g, "data", None)
|
||||
if data is not None:
|
||||
data.close()
|
||||
|
||||
@ -69,11 +72,12 @@ def authrequired(func: Callable) -> Callable:
|
||||
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
||||
if not g.authorized:
|
||||
return jsonify_response(
|
||||
{'error': 'Unauthorized client!'},
|
||||
{"error": "Unauthorized client!"},
|
||||
401,
|
||||
)
|
||||
else:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return decoratedfunction
|
||||
|
||||
|
||||
@ -81,27 +85,30 @@ def jsonify(func: Callable) -> Callable:
|
||||
@wraps(func)
|
||||
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
||||
return jsonify_response(func(*args, **kwargs))
|
||||
|
||||
return decoratedfunction
|
||||
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def server_exception(exception: Any) -> Response:
|
||||
stack = ''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))
|
||||
stack = "".join(
|
||||
traceback.format_exception(type(exception), exception, exception.__traceback__)
|
||||
)
|
||||
print(stack)
|
||||
try:
|
||||
g.data.local.network.put_event(
|
||||
'exception',
|
||||
"exception",
|
||||
{
|
||||
'service': 'api',
|
||||
'request': request.url,
|
||||
'traceback': stack,
|
||||
"service": "api",
|
||||
"request": request.url,
|
||||
"traceback": stack,
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify_response(
|
||||
{'error': 'Exception occured while processing request.'},
|
||||
{"error": "Exception occured while processing request."},
|
||||
500,
|
||||
)
|
||||
|
||||
@ -109,7 +116,7 @@ def server_exception(exception: Any) -> Response:
|
||||
@app.errorhandler(APIException)
|
||||
def api_exception(exception: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': exception.message},
|
||||
{"error": exception.message},
|
||||
exception.code,
|
||||
)
|
||||
|
||||
@ -117,7 +124,7 @@ def api_exception(exception: Any) -> Response:
|
||||
@app.errorhandler(500)
|
||||
def server_error(error: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': 'Exception occured while processing request.'},
|
||||
{"error": "Exception occured while processing request."},
|
||||
500,
|
||||
)
|
||||
|
||||
@ -125,7 +132,7 @@ def server_error(error: Any) -> Response:
|
||||
@app.errorhandler(501)
|
||||
def protocol_error(error: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': 'Unsupported protocol version in request.'},
|
||||
{"error": "Unsupported protocol version in request."},
|
||||
501,
|
||||
)
|
||||
|
||||
@ -133,7 +140,7 @@ def protocol_error(error: Any) -> Response:
|
||||
@app.errorhandler(400)
|
||||
def bad_json(error: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': 'Request JSON could not be decoded.'},
|
||||
{"error": "Request JSON could not be decoded."},
|
||||
500,
|
||||
)
|
||||
|
||||
@ -141,7 +148,7 @@ def bad_json(error: Any) -> Response:
|
||||
@app.errorhandler(404)
|
||||
def unrecognized_object(error: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': 'Unrecognized request game/version or object.'},
|
||||
{"error": "Unrecognized request game/version or object."},
|
||||
404,
|
||||
)
|
||||
|
||||
@ -149,50 +156,50 @@ def unrecognized_object(error: Any) -> Response:
|
||||
@app.errorhandler(405)
|
||||
def invalid_request(error: Any) -> Response:
|
||||
return jsonify_response(
|
||||
{'error': 'Invalid request URI or method.'},
|
||||
{"error": "Invalid request URI or method."},
|
||||
405,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/<path:path>', methods=['GET', 'POST'])
|
||||
@app.route("/<path:path>", methods=["GET", "POST"])
|
||||
@authrequired
|
||||
def catch_all(path: str) -> Response:
|
||||
abort(405)
|
||||
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
@app.route("/", methods=["GET", "POST"])
|
||||
@authrequired
|
||||
@jsonify
|
||||
def info() -> Dict[str, Any]:
|
||||
requestdata = request.get_json()
|
||||
if requestdata is None:
|
||||
raise APIException('Request JSON could not be decoded.')
|
||||
raise APIException("Request JSON could not be decoded.")
|
||||
if requestdata:
|
||||
raise APIException('Unrecognized parameters for request.')
|
||||
raise APIException("Unrecognized parameters for request.")
|
||||
|
||||
return {
|
||||
'versions': SUPPORTED_VERSIONS,
|
||||
'name': g.config.name,
|
||||
'email': g.config.email,
|
||||
"versions": SUPPORTED_VERSIONS,
|
||||
"name": g.config.name,
|
||||
"email": g.config.email,
|
||||
}
|
||||
|
||||
|
||||
@app.route('/<protoversion>/<requestgame>/<requestversion>', methods=['GET', 'POST'])
|
||||
@app.route("/<protoversion>/<requestgame>/<requestversion>", methods=["GET", "POST"])
|
||||
@authrequired
|
||||
@jsonify
|
||||
def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str, Any]:
|
||||
requestdata = request.get_json()
|
||||
for expected in ['type', 'ids', 'objects']:
|
||||
for expected in ["type", "ids", "objects"]:
|
||||
if expected not in requestdata:
|
||||
raise APIException('Missing parameters for request.')
|
||||
raise APIException("Missing parameters for request.")
|
||||
for param in requestdata:
|
||||
if param not in ['type', 'ids', 'objects', 'since', 'until']:
|
||||
raise APIException('Unrecognized parameters for request.')
|
||||
if param not in ["type", "ids", "objects", "since", "until"]:
|
||||
raise APIException("Unrecognized parameters for request.")
|
||||
|
||||
args = copy.deepcopy(requestdata)
|
||||
del args['type']
|
||||
del args['ids']
|
||||
del args['objects']
|
||||
del args["type"]
|
||||
del args["ids"]
|
||||
del args["objects"]
|
||||
|
||||
if protoversion not in SUPPORTED_VERSIONS:
|
||||
# Don't know about this protocol version
|
||||
@ -201,13 +208,13 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str
|
||||
# Figure out what games we support based on config, and map those.
|
||||
gamemapping = {}
|
||||
for (gameid, constant) in [
|
||||
('ddr', GameConstants.DDR),
|
||||
('iidx', GameConstants.IIDX),
|
||||
('jubeat', GameConstants.JUBEAT),
|
||||
('museca', GameConstants.MUSECA),
|
||||
('popnmusic', GameConstants.POPN_MUSIC),
|
||||
('reflecbeat', GameConstants.REFLEC_BEAT),
|
||||
('soundvoltex', GameConstants.SDVX),
|
||||
("ddr", GameConstants.DDR),
|
||||
("iidx", GameConstants.IIDX),
|
||||
("jubeat", GameConstants.JUBEAT),
|
||||
("museca", GameConstants.MUSECA),
|
||||
("popnmusic", GameConstants.POPN_MUSIC),
|
||||
("reflecbeat", GameConstants.REFLEC_BEAT),
|
||||
("soundvoltex", GameConstants.SDVX),
|
||||
]:
|
||||
if constant in g.config.support:
|
||||
gamemapping[gameid] = constant
|
||||
@ -216,73 +223,77 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str
|
||||
# Don't support this game!
|
||||
abort(404)
|
||||
|
||||
if requestversion[0] == 'o':
|
||||
if requestversion[0] == "o":
|
||||
omnimix = True
|
||||
requestversion = requestversion[1:]
|
||||
else:
|
||||
omnimix = False
|
||||
|
||||
version = {
|
||||
GameConstants.DDR: {
|
||||
'12': VersionConstants.DDR_X2,
|
||||
'13': VersionConstants.DDR_X3_VS_2NDMIX,
|
||||
'14': VersionConstants.DDR_2013,
|
||||
'15': VersionConstants.DDR_2014,
|
||||
'16': VersionConstants.DDR_ACE,
|
||||
},
|
||||
GameConstants.IIDX: {
|
||||
'20': VersionConstants.IIDX_TRICORO,
|
||||
'21': VersionConstants.IIDX_SPADA,
|
||||
'22': VersionConstants.IIDX_PENDUAL,
|
||||
'23': VersionConstants.IIDX_COPULA,
|
||||
'24': VersionConstants.IIDX_SINOBUZ,
|
||||
'25': VersionConstants.IIDX_CANNON_BALLERS,
|
||||
'26': VersionConstants.IIDX_ROOTAGE,
|
||||
},
|
||||
GameConstants.JUBEAT: {
|
||||
'5': VersionConstants.JUBEAT_SAUCER,
|
||||
'5a': VersionConstants.JUBEAT_SAUCER_FULFILL,
|
||||
'6': VersionConstants.JUBEAT_PROP,
|
||||
'7': VersionConstants.JUBEAT_QUBELL,
|
||||
'8': VersionConstants.JUBEAT_CLAN,
|
||||
'9': VersionConstants.JUBEAT_FESTO,
|
||||
},
|
||||
GameConstants.MUSECA: {
|
||||
'1': VersionConstants.MUSECA,
|
||||
'1p': VersionConstants.MUSECA_1_PLUS,
|
||||
},
|
||||
GameConstants.POPN_MUSIC: {
|
||||
'19': VersionConstants.POPN_MUSIC_TUNE_STREET,
|
||||
'20': VersionConstants.POPN_MUSIC_FANTASIA,
|
||||
'21': VersionConstants.POPN_MUSIC_SUNNY_PARK,
|
||||
'22': VersionConstants.POPN_MUSIC_LAPISTORIA,
|
||||
'23': VersionConstants.POPN_MUSIC_ECLALE,
|
||||
'24': VersionConstants.POPN_MUSIC_USANEKO,
|
||||
},
|
||||
GameConstants.REFLEC_BEAT: {
|
||||
'1': VersionConstants.REFLEC_BEAT,
|
||||
'2': VersionConstants.REFLEC_BEAT_LIMELIGHT,
|
||||
# We don't support non-final COLETTE, so just return scores for
|
||||
# final colette to any network that asks.
|
||||
'3w': VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
'3sp': VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
'3su': VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
'3a': VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
'3as': VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
# We don't support groovin'!!, so just return upper scores.
|
||||
'4': VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||
'4u': VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||
'5': VersionConstants.REFLEC_BEAT_VOLZZA,
|
||||
'5a': VersionConstants.REFLEC_BEAT_VOLZZA_2,
|
||||
'6': VersionConstants.REFLEC_BEAT_REFLESIA,
|
||||
},
|
||||
GameConstants.SDVX: {
|
||||
'1': VersionConstants.SDVX_BOOTH,
|
||||
'2': VersionConstants.SDVX_INFINITE_INFECTION,
|
||||
'3': VersionConstants.SDVX_GRAVITY_WARS,
|
||||
'4': VersionConstants.SDVX_HEAVENLY_HAVEN,
|
||||
},
|
||||
}.get(game, {}).get(requestversion)
|
||||
version = (
|
||||
{
|
||||
GameConstants.DDR: {
|
||||
"12": VersionConstants.DDR_X2,
|
||||
"13": VersionConstants.DDR_X3_VS_2NDMIX,
|
||||
"14": VersionConstants.DDR_2013,
|
||||
"15": VersionConstants.DDR_2014,
|
||||
"16": VersionConstants.DDR_ACE,
|
||||
},
|
||||
GameConstants.IIDX: {
|
||||
"20": VersionConstants.IIDX_TRICORO,
|
||||
"21": VersionConstants.IIDX_SPADA,
|
||||
"22": VersionConstants.IIDX_PENDUAL,
|
||||
"23": VersionConstants.IIDX_COPULA,
|
||||
"24": VersionConstants.IIDX_SINOBUZ,
|
||||
"25": VersionConstants.IIDX_CANNON_BALLERS,
|
||||
"26": VersionConstants.IIDX_ROOTAGE,
|
||||
},
|
||||
GameConstants.JUBEAT: {
|
||||
"5": VersionConstants.JUBEAT_SAUCER,
|
||||
"5a": VersionConstants.JUBEAT_SAUCER_FULFILL,
|
||||
"6": VersionConstants.JUBEAT_PROP,
|
||||
"7": VersionConstants.JUBEAT_QUBELL,
|
||||
"8": VersionConstants.JUBEAT_CLAN,
|
||||
"9": VersionConstants.JUBEAT_FESTO,
|
||||
},
|
||||
GameConstants.MUSECA: {
|
||||
"1": VersionConstants.MUSECA,
|
||||
"1p": VersionConstants.MUSECA_1_PLUS,
|
||||
},
|
||||
GameConstants.POPN_MUSIC: {
|
||||
"19": VersionConstants.POPN_MUSIC_TUNE_STREET,
|
||||
"20": VersionConstants.POPN_MUSIC_FANTASIA,
|
||||
"21": VersionConstants.POPN_MUSIC_SUNNY_PARK,
|
||||
"22": VersionConstants.POPN_MUSIC_LAPISTORIA,
|
||||
"23": VersionConstants.POPN_MUSIC_ECLALE,
|
||||
"24": VersionConstants.POPN_MUSIC_USANEKO,
|
||||
},
|
||||
GameConstants.REFLEC_BEAT: {
|
||||
"1": VersionConstants.REFLEC_BEAT,
|
||||
"2": VersionConstants.REFLEC_BEAT_LIMELIGHT,
|
||||
# We don't support non-final COLETTE, so just return scores for
|
||||
# final colette to any network that asks.
|
||||
"3w": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
"3sp": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
"3su": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
"3a": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
"3as": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||
# We don't support groovin'!!, so just return upper scores.
|
||||
"4": VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||
"4u": VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||
"5": VersionConstants.REFLEC_BEAT_VOLZZA,
|
||||
"5a": VersionConstants.REFLEC_BEAT_VOLZZA_2,
|
||||
"6": VersionConstants.REFLEC_BEAT_REFLESIA,
|
||||
},
|
||||
GameConstants.SDVX: {
|
||||
"1": VersionConstants.SDVX_BOOTH,
|
||||
"2": VersionConstants.SDVX_INFINITE_INFECTION,
|
||||
"3": VersionConstants.SDVX_GRAVITY_WARS,
|
||||
"4": VersionConstants.SDVX_HEAVENLY_HAVEN,
|
||||
},
|
||||
}
|
||||
.get(game, {})
|
||||
.get(requestversion)
|
||||
)
|
||||
if version is None:
|
||||
# Don't support this version!
|
||||
abort(404)
|
||||
@ -290,30 +301,30 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str
|
||||
# Attempt to coerce ID type. If we fail, provide the correct failure message.
|
||||
idtype = None
|
||||
try:
|
||||
idtype = APIConstants(requestdata['type'])
|
||||
idtype = APIConstants(requestdata["type"])
|
||||
except ValueError:
|
||||
pass
|
||||
if idtype is None:
|
||||
raise APIException('Invalid ID type provided!')
|
||||
raise APIException("Invalid ID type provided!")
|
||||
|
||||
# Validate the provided IDs given the ID type above.
|
||||
ids = requestdata['ids']
|
||||
ids = requestdata["ids"]
|
||||
if idtype == APIConstants.ID_TYPE_CARD and len(ids) == 0:
|
||||
raise APIException('Invalid number of IDs given!')
|
||||
raise APIException("Invalid number of IDs given!")
|
||||
if idtype == APIConstants.ID_TYPE_SONG and len(ids) not in [1, 2]:
|
||||
raise APIException('Invalid number of IDs given!')
|
||||
raise APIException("Invalid number of IDs given!")
|
||||
if idtype == APIConstants.ID_TYPE_INSTANCE and len(ids) != 3:
|
||||
raise APIException('Invalid number of IDs given!')
|
||||
raise APIException("Invalid number of IDs given!")
|
||||
if idtype == APIConstants.ID_TYPE_SERVER and len(ids) != 0:
|
||||
raise APIException('Invalid number of IDs given!')
|
||||
raise APIException("Invalid number of IDs given!")
|
||||
|
||||
responsedata = {}
|
||||
for obj in requestdata['objects']:
|
||||
for obj in requestdata["objects"]:
|
||||
handler = {
|
||||
'records': RecordsObject,
|
||||
'profile': ProfileObject,
|
||||
'statistics': StatisticsObject,
|
||||
'catalog': CatalogObject,
|
||||
"records": RecordsObject,
|
||||
"profile": ProfileObject,
|
||||
"statistics": StatisticsObject,
|
||||
"catalog": CatalogObject,
|
||||
}.get(obj)
|
||||
if handler is None:
|
||||
# Don't support this object type
|
||||
@ -321,7 +332,7 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str
|
||||
|
||||
inst = handler(g.data, game, version, omnimix)
|
||||
try:
|
||||
fetchmethod = getattr(inst, f'fetch_{protoversion}')
|
||||
fetchmethod = getattr(inst, f"fetch_{protoversion}")
|
||||
except AttributeError:
|
||||
# Don't know how to handle this object for this version
|
||||
abort(501)
|
||||
|
@ -1,5 +1,4 @@
|
||||
class APIException(Exception):
|
||||
|
||||
def __init__(self, msg: str, code: int=500) -> None:
|
||||
def __init__(self, msg: str, code: int = 500) -> None:
|
||||
self.message = msg
|
||||
self.code = code
|
||||
|
@ -14,11 +14,15 @@ class BaseObject:
|
||||
various fetch versions.
|
||||
"""
|
||||
|
||||
def __init__(self, data: Data, game: GameConstants, version: int, omnimix: bool) -> None:
|
||||
def __init__(
|
||||
self, data: Data, game: GameConstants, version: int, omnimix: bool
|
||||
) -> None:
|
||||
self.data = data
|
||||
self.game = game
|
||||
self.version = version
|
||||
self.omnimix = omnimix
|
||||
|
||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> Any:
|
||||
raise APIException('Object fetch not supported for this version!')
|
||||
def fetch_v1(
|
||||
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||
) -> Any:
|
||||
raise APIException("Object fetch not supported for this version!")
|
||||
|
@ -7,31 +7,30 @@ from bemani.data import Song
|
||||
|
||||
|
||||
class CatalogObject(BaseObject):
|
||||
|
||||
def __format_ddr_song(self, song: Song) -> Dict[str, Any]:
|
||||
groove = song.data.get_dict('groove')
|
||||
groove = song.data.get_dict("groove")
|
||||
return {
|
||||
'editid': str(song.data.get_int('edit_id')),
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'bpm_min': song.data.get_int('bpm_min'),
|
||||
'bpm_max': song.data.get_int('bpm_max'),
|
||||
'category': str(song.data.get_int('category')),
|
||||
'groove': {
|
||||
'air': groove.get_int('air'),
|
||||
'chaos': groove.get_int('chaos'),
|
||||
'freeze': groove.get_int('freeze'),
|
||||
'stream': groove.get_int('stream'),
|
||||
'voltage': groove.get_int('voltage'),
|
||||
"editid": str(song.data.get_int("edit_id")),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"bpm_min": song.data.get_int("bpm_min"),
|
||||
"bpm_max": song.data.get_int("bpm_max"),
|
||||
"category": str(song.data.get_int("category")),
|
||||
"groove": {
|
||||
"air": groove.get_int("air"),
|
||||
"chaos": groove.get_int("chaos"),
|
||||
"freeze": groove.get_int("freeze"),
|
||||
"stream": groove.get_int("stream"),
|
||||
"voltage": groove.get_int("voltage"),
|
||||
},
|
||||
}
|
||||
|
||||
def __format_iidx_song(self, song: Song) -> Dict[str, Any]:
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'bpm_min': song.data.get_int('bpm_min'),
|
||||
'bpm_max': song.data.get_int('bpm_max'),
|
||||
'notecount': song.data.get_int('notecount'),
|
||||
'category': str(int(song.id / 1000)),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"bpm_min": song.data.get_int("bpm_min"),
|
||||
"bpm_max": song.data.get_int("bpm_max"),
|
||||
"notecount": song.data.get_int("notecount"),
|
||||
"category": str(int(song.id / 1000)),
|
||||
}
|
||||
|
||||
def __format_jubeat_song(self, song: Song) -> Dict[str, Any]:
|
||||
@ -46,67 +45,69 @@ class CatalogObject(BaseObject):
|
||||
6: VersionConstants.JUBEAT_PROP,
|
||||
7: VersionConstants.JUBEAT_QUBELL,
|
||||
8: VersionConstants.JUBEAT_CLAN,
|
||||
9: VersionConstants.JUBEAT_FESTO
|
||||
9: VersionConstants.JUBEAT_FESTO,
|
||||
}.get(int(song.id / 10000000), VersionConstants.JUBEAT)
|
||||
# Map the category to the version numbers defined on BEMAPI.
|
||||
categorymapping = {
|
||||
VersionConstants.JUBEAT: '1',
|
||||
VersionConstants.JUBEAT_RIPPLES: '2',
|
||||
VersionConstants.JUBEAT_RIPPLES_APPEND: '2a',
|
||||
VersionConstants.JUBEAT_KNIT: '3',
|
||||
VersionConstants.JUBEAT_KNIT_APPEND: '3a',
|
||||
VersionConstants.JUBEAT_COPIOUS: '4',
|
||||
VersionConstants.JUBEAT_COPIOUS_APPEND: '4a',
|
||||
VersionConstants.JUBEAT_SAUCER: '5',
|
||||
VersionConstants.JUBEAT_SAUCER_FULFILL: '5a',
|
||||
VersionConstants.JUBEAT_PROP: '6',
|
||||
VersionConstants.JUBEAT_QUBELL: '7',
|
||||
VersionConstants.JUBEAT_CLAN: '8',
|
||||
VersionConstants.JUBEAT_FESTO: '9',
|
||||
VersionConstants.JUBEAT: "1",
|
||||
VersionConstants.JUBEAT_RIPPLES: "2",
|
||||
VersionConstants.JUBEAT_RIPPLES_APPEND: "2a",
|
||||
VersionConstants.JUBEAT_KNIT: "3",
|
||||
VersionConstants.JUBEAT_KNIT_APPEND: "3a",
|
||||
VersionConstants.JUBEAT_COPIOUS: "4",
|
||||
VersionConstants.JUBEAT_COPIOUS_APPEND: "4a",
|
||||
VersionConstants.JUBEAT_SAUCER: "5",
|
||||
VersionConstants.JUBEAT_SAUCER_FULFILL: "5a",
|
||||
VersionConstants.JUBEAT_PROP: "6",
|
||||
VersionConstants.JUBEAT_QUBELL: "7",
|
||||
VersionConstants.JUBEAT_CLAN: "8",
|
||||
VersionConstants.JUBEAT_FESTO: "9",
|
||||
}
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'category': categorymapping.get(song.data.get_int('version', defaultcategory), '1'),
|
||||
'bpm_min': song.data.get_int('bpm_min'),
|
||||
'bpm_max': song.data.get_int('bpm_max'),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"category": categorymapping.get(
|
||||
song.data.get_int("version", defaultcategory), "1"
|
||||
),
|
||||
"bpm_min": song.data.get_int("bpm_min"),
|
||||
"bpm_max": song.data.get_int("bpm_max"),
|
||||
}
|
||||
|
||||
def __format_museca_song(self, song: Song) -> Dict[str, Any]:
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'bpm_min': song.data.get_int('bpm_min'),
|
||||
'bpm_max': song.data.get_int('bpm_max'),
|
||||
'limited': song.data.get_int('limited'),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"bpm_min": song.data.get_int("bpm_min"),
|
||||
"bpm_max": song.data.get_int("bpm_max"),
|
||||
"limited": song.data.get_int("limited"),
|
||||
}
|
||||
|
||||
def __format_popn_song(self, song: Song) -> Dict[str, Any]:
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'category': song.data.get_str('category'),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"category": song.data.get_str("category"),
|
||||
}
|
||||
|
||||
def __format_reflec_song(self, song: Song) -> Dict[str, Any]:
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'category': str(song.data.get_int('folder')),
|
||||
'musicid': song.data.get_str('chart_id'),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"category": str(song.data.get_int("folder")),
|
||||
"musicid": song.data.get_str("chart_id"),
|
||||
}
|
||||
|
||||
def __format_sdvx_song(self, song: Song) -> Dict[str, Any]:
|
||||
return {
|
||||
'difficulty': song.data.get_int('difficulty'),
|
||||
'bpm_min': song.data.get_int('bpm_min'),
|
||||
'bpm_max': song.data.get_int('bpm_max'),
|
||||
'limited': song.data.get_int('limited'),
|
||||
"difficulty": song.data.get_int("difficulty"),
|
||||
"bpm_min": song.data.get_int("bpm_min"),
|
||||
"bpm_max": song.data.get_int("bpm_max"),
|
||||
"limited": song.data.get_int("limited"),
|
||||
}
|
||||
|
||||
def __format_song(self, song: Song) -> Dict[str, Any]:
|
||||
base = {
|
||||
'song': str(song.id),
|
||||
'chart': str(song.chart),
|
||||
'title': song.name or "",
|
||||
'artist': song.artist or "",
|
||||
'genre': song.genre or "",
|
||||
"song": str(song.id),
|
||||
"chart": str(song.chart),
|
||||
"title": song.name or "",
|
||||
"artist": song.artist or "",
|
||||
"genre": song.genre or "",
|
||||
}
|
||||
|
||||
if self.game == GameConstants.DDR:
|
||||
@ -198,7 +199,7 @@ class CatalogObject(BaseObject):
|
||||
"type": item.type[3:],
|
||||
}
|
||||
for item in items
|
||||
if item.type in ['qp_body', 'qp_face', 'qp_hair', 'qp_hand', 'qp_head']
|
||||
if item.type in ["qp_body", "qp_face", "qp_hair", "qp_hand", "qp_head"]
|
||||
],
|
||||
}
|
||||
|
||||
@ -222,17 +223,22 @@ class CatalogObject(BaseObject):
|
||||
else:
|
||||
return self.version
|
||||
|
||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> Dict[str, List[Dict[str, Any]]]:
|
||||
def fetch_v1(
|
||||
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||
) -> Dict[str, List[Dict[str, Any]]]:
|
||||
# Verify IDs
|
||||
if idtype != APIConstants.ID_TYPE_SERVER:
|
||||
raise APIException(
|
||||
'Unsupported ID for lookup!',
|
||||
"Unsupported ID for lookup!",
|
||||
405,
|
||||
)
|
||||
|
||||
# Fetch the songs
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||
if self.game == GameConstants.JUBEAT and self.version == VersionConstants.JUBEAT_CLAN:
|
||||
if (
|
||||
self.game == GameConstants.JUBEAT
|
||||
and self.version == VersionConstants.JUBEAT_CLAN
|
||||
):
|
||||
# There's always a special case. We don't store all music IDs since those in
|
||||
# the range of 80000301-80000347 are actually the same song, but copy-pasted
|
||||
# for different prefectures and slightly different charts. So, we need to copy
|
||||
@ -255,7 +261,7 @@ class CatalogObject(BaseObject):
|
||||
)
|
||||
songs.extend(additions)
|
||||
retval = {
|
||||
'songs': [self.__format_song(song) for song in songs],
|
||||
"songs": [self.__format_song(song) for song in songs],
|
||||
}
|
||||
|
||||
# Fetch any optional extras per-game, return
|
||||
|
@ -7,24 +7,23 @@ from bemani.data import UserID
|
||||
|
||||
|
||||
class ProfileObject(BaseObject):
|
||||
|
||||
def __format_ddr_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
return {
|
||||
'area': profile.get_int('area', -1) if exact else -1,
|
||||
"area": profile.get_int("area", -1) if exact else -1,
|
||||
}
|
||||
|
||||
def __format_iidx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
qpro = profile.get_dict('qpro')
|
||||
qpro = profile.get_dict("qpro")
|
||||
|
||||
return {
|
||||
'area': profile.get_int('pid', -1),
|
||||
'qpro': {
|
||||
'head': qpro.get_int('head', -1) if exact else -1,
|
||||
'hair': qpro.get_int('hair', -1) if exact else -1,
|
||||
'face': qpro.get_int('face', -1) if exact else -1,
|
||||
'body': qpro.get_int('body', -1) if exact else -1,
|
||||
'hand': qpro.get_int('hand', -1) if exact else -1,
|
||||
}
|
||||
"area": profile.get_int("pid", -1),
|
||||
"qpro": {
|
||||
"head": qpro.get_int("head", -1) if exact else -1,
|
||||
"hair": qpro.get_int("hair", -1) if exact else -1,
|
||||
"face": qpro.get_int("face", -1) if exact else -1,
|
||||
"body": qpro.get_int("body", -1) if exact else -1,
|
||||
"hand": qpro.get_int("hand", -1) if exact else -1,
|
||||
},
|
||||
}
|
||||
|
||||
def __format_jubeat_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
@ -35,25 +34,27 @@ class ProfileObject(BaseObject):
|
||||
|
||||
def __format_popn_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
return {
|
||||
'character': profile.get_int('chara', -1) if exact else -1,
|
||||
"character": profile.get_int("chara", -1) if exact else -1,
|
||||
}
|
||||
|
||||
def __format_reflec_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
return {
|
||||
'icon': profile.get_dict('config').get_int('icon_id', -1) if exact else -1,
|
||||
"icon": profile.get_dict("config").get_int("icon_id", -1) if exact else -1,
|
||||
}
|
||||
|
||||
def __format_sdvx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
def __format_profile(self, cardids: List[str], profile: Profile, settings: ValidatedDict, exact: bool) -> Dict[str, Any]:
|
||||
def __format_profile(
|
||||
self, cardids: List[str], profile: Profile, settings: ValidatedDict, exact: bool
|
||||
) -> Dict[str, Any]:
|
||||
base = {
|
||||
'name': profile.get_str('name'),
|
||||
'cards': cardids,
|
||||
'registered': settings.get_int('first_play_timestamp', -1),
|
||||
'updated': settings.get_int('last_play_timestamp', -1),
|
||||
'plays': settings.get_int('total_plays', -1),
|
||||
'match': 'exact' if exact else 'partial',
|
||||
"name": profile.get_str("name"),
|
||||
"cards": cardids,
|
||||
"registered": settings.get_int("first_play_timestamp", -1),
|
||||
"updated": settings.get_int("last_play_timestamp", -1),
|
||||
"plays": settings.get_int("total_plays", -1),
|
||||
"match": "exact" if exact else "partial",
|
||||
}
|
||||
|
||||
if self.game == GameConstants.DDR:
|
||||
@ -73,19 +74,23 @@ class ProfileObject(BaseObject):
|
||||
|
||||
return base
|
||||
|
||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
def fetch_v1(
|
||||
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
# Fetch the profiles
|
||||
profiles: List[Tuple[UserID, Profile]] = []
|
||||
if idtype == APIConstants.ID_TYPE_SERVER:
|
||||
profiles.extend(self.data.local.user.get_all_profiles(self.game, self.version))
|
||||
profiles.extend(
|
||||
self.data.local.user.get_all_profiles(self.game, self.version)
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||
raise APIException(
|
||||
'Unsupported ID for lookup!',
|
||||
"Unsupported ID for lookup!",
|
||||
405,
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||
raise APIException(
|
||||
'Unsupported ID for lookup!',
|
||||
"Unsupported ID for lookup!",
|
||||
405,
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_CARD:
|
||||
@ -103,11 +108,13 @@ class ProfileObject(BaseObject):
|
||||
# in the case that we returned scores for a user that doesn't have a
|
||||
# profile on a particular version. We allow that on this network, so in
|
||||
# order to not break remote networks, try our best to return any profile.
|
||||
profile = self.data.local.user.get_any_profile(self.game, self.version, userid)
|
||||
profile = self.data.local.user.get_any_profile(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
if profile is not None:
|
||||
profiles.append((userid, profile))
|
||||
else:
|
||||
raise APIException('Invalid ID type!')
|
||||
raise APIException("Invalid ID type!")
|
||||
|
||||
# Now, fetch the users, and filter out profiles belonging to orphaned users
|
||||
retval: List[Dict[str, Any]] = []
|
||||
@ -126,6 +133,13 @@ class ProfileObject(BaseObject):
|
||||
if settings is None:
|
||||
settings = ValidatedDict({})
|
||||
|
||||
retval.append(self.__format_profile(id_to_cards[userid], profile, settings, profile.version == self.version))
|
||||
retval.append(
|
||||
self.__format_profile(
|
||||
id_to_cards[userid],
|
||||
profile,
|
||||
settings,
|
||||
profile.version == self.version,
|
||||
)
|
||||
)
|
||||
|
||||
return retval
|
||||
|
@ -7,15 +7,14 @@ from bemani.data import Score, UserID
|
||||
|
||||
|
||||
class RecordsObject(BaseObject):
|
||||
|
||||
def __format_ddr_record(self, record: Score) -> Dict[str, Any]:
|
||||
halo = {
|
||||
DBConstants.DDR_HALO_NONE: 'none',
|
||||
DBConstants.DDR_HALO_GOOD_FULL_COMBO: 'gfc',
|
||||
DBConstants.DDR_HALO_GREAT_FULL_COMBO: 'fc',
|
||||
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: 'pfc',
|
||||
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: 'mfc',
|
||||
}.get(record.data.get_int('halo'), 'none')
|
||||
DBConstants.DDR_HALO_NONE: "none",
|
||||
DBConstants.DDR_HALO_GOOD_FULL_COMBO: "gfc",
|
||||
DBConstants.DDR_HALO_GREAT_FULL_COMBO: "fc",
|
||||
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: "pfc",
|
||||
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: "mfc",
|
||||
}.get(record.data.get_int("halo"), "none")
|
||||
rank = {
|
||||
DBConstants.DDR_RANK_AAA: "AAA",
|
||||
DBConstants.DDR_RANK_AA_PLUS: "AA+",
|
||||
@ -33,175 +32,175 @@ class RecordsObject(BaseObject):
|
||||
DBConstants.DDR_RANK_D_PLUS: "D+",
|
||||
DBConstants.DDR_RANK_D: "D",
|
||||
DBConstants.DDR_RANK_E: "E",
|
||||
}.get(record.data.get_int('rank'), 'E')
|
||||
}.get(record.data.get_int("rank"), "E")
|
||||
|
||||
if self.version == VersionConstants.DDR_ACE:
|
||||
# DDR Ace is specia
|
||||
ghost = [int(x) for x in record.data.get_str('ghost')]
|
||||
ghost = [int(x) for x in record.data.get_str("ghost")]
|
||||
else:
|
||||
if 'trace' not in record.data:
|
||||
if "trace" not in record.data:
|
||||
ghost = []
|
||||
else:
|
||||
ghost = record.data.get_int_array('trace', len(record.data['trace']))
|
||||
ghost = record.data.get_int_array("trace", len(record.data["trace"]))
|
||||
|
||||
return {
|
||||
'rank': rank,
|
||||
'halo': halo,
|
||||
'combo': record.data.get_int('combo'),
|
||||
'ghost': ghost,
|
||||
"rank": rank,
|
||||
"halo": halo,
|
||||
"combo": record.data.get_int("combo"),
|
||||
"ghost": ghost,
|
||||
}
|
||||
|
||||
def __format_iidx_record(self, record: Score) -> Dict[str, Any]:
|
||||
status = {
|
||||
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: 'np',
|
||||
DBConstants.IIDX_CLEAR_STATUS_FAILED: 'failed',
|
||||
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: 'ac',
|
||||
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: 'ec',
|
||||
DBConstants.IIDX_CLEAR_STATUS_CLEAR: 'nc',
|
||||
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: 'hc',
|
||||
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: 'exhc',
|
||||
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: 'fc',
|
||||
}.get(record.data.get_int('clear_status'), 'np')
|
||||
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: "np",
|
||||
DBConstants.IIDX_CLEAR_STATUS_FAILED: "failed",
|
||||
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: "ac",
|
||||
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: "ec",
|
||||
DBConstants.IIDX_CLEAR_STATUS_CLEAR: "nc",
|
||||
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: "hc",
|
||||
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: "exhc",
|
||||
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: "fc",
|
||||
}.get(record.data.get_int("clear_status"), "np")
|
||||
|
||||
return {
|
||||
'status': status,
|
||||
'miss': record.data.get_int('miss_count', -1),
|
||||
'ghost': [b for b in record.data.get_bytes('ghost')],
|
||||
'pgreat': record.data.get_int('pgreats', -1),
|
||||
'great': record.data.get_int('greats', -1),
|
||||
"status": status,
|
||||
"miss": record.data.get_int("miss_count", -1),
|
||||
"ghost": [b for b in record.data.get_bytes("ghost")],
|
||||
"pgreat": record.data.get_int("pgreats", -1),
|
||||
"great": record.data.get_int("greats", -1),
|
||||
}
|
||||
|
||||
def __format_jubeat_record(self, record: Score) -> Dict[str, Any]:
|
||||
status = {
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: 'failed',
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: 'cleared',
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: 'nfc',
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: 'fc',
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: 'nec',
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: 'exc',
|
||||
}.get(record.data.get_int('medal'), 'failed')
|
||||
if 'ghost' not in record.data:
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: "failed",
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: "cleared",
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: "nfc",
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: "fc",
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: "nec",
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: "exc",
|
||||
}.get(record.data.get_int("medal"), "failed")
|
||||
if "ghost" not in record.data:
|
||||
ghost: List[int] = []
|
||||
else:
|
||||
ghost = record.data.get_int_array('ghost', len(record.data['ghost']))
|
||||
ghost = record.data.get_int_array("ghost", len(record.data["ghost"]))
|
||||
|
||||
return {
|
||||
'status': status,
|
||||
'combo': record.data.get_int('combo', -1),
|
||||
'ghost': ghost,
|
||||
'music_rate': record.data.get_int('music_rate'),
|
||||
"status": status,
|
||||
"combo": record.data.get_int("combo", -1),
|
||||
"ghost": ghost,
|
||||
"music_rate": record.data.get_int("music_rate"),
|
||||
}
|
||||
|
||||
def __format_museca_record(self, record: Score) -> Dict[str, Any]:
|
||||
rank = {
|
||||
DBConstants.MUSECA_GRADE_DEATH: 'death',
|
||||
DBConstants.MUSECA_GRADE_POOR: 'poor',
|
||||
DBConstants.MUSECA_GRADE_MEDIOCRE: 'mediocre',
|
||||
DBConstants.MUSECA_GRADE_GOOD: 'good',
|
||||
DBConstants.MUSECA_GRADE_GREAT: 'great',
|
||||
DBConstants.MUSECA_GRADE_EXCELLENT: 'excellent',
|
||||
DBConstants.MUSECA_GRADE_SUPERB: 'superb',
|
||||
DBConstants.MUSECA_GRADE_MASTERPIECE: 'masterpiece',
|
||||
DBConstants.MUSECA_GRADE_PERFECT: 'perfect'
|
||||
}.get(record.data.get_int('grade'), 'death')
|
||||
DBConstants.MUSECA_GRADE_DEATH: "death",
|
||||
DBConstants.MUSECA_GRADE_POOR: "poor",
|
||||
DBConstants.MUSECA_GRADE_MEDIOCRE: "mediocre",
|
||||
DBConstants.MUSECA_GRADE_GOOD: "good",
|
||||
DBConstants.MUSECA_GRADE_GREAT: "great",
|
||||
DBConstants.MUSECA_GRADE_EXCELLENT: "excellent",
|
||||
DBConstants.MUSECA_GRADE_SUPERB: "superb",
|
||||
DBConstants.MUSECA_GRADE_MASTERPIECE: "masterpiece",
|
||||
DBConstants.MUSECA_GRADE_PERFECT: "perfect",
|
||||
}.get(record.data.get_int("grade"), "death")
|
||||
status = {
|
||||
DBConstants.MUSECA_CLEAR_TYPE_FAILED: 'failed',
|
||||
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: 'cleared',
|
||||
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: 'fc',
|
||||
}.get(record.data.get_int('clear_type'), 'failed')
|
||||
DBConstants.MUSECA_CLEAR_TYPE_FAILED: "failed",
|
||||
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: "cleared",
|
||||
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: "fc",
|
||||
}.get(record.data.get_int("clear_type"), "failed")
|
||||
|
||||
return {
|
||||
'rank': rank,
|
||||
'status': status,
|
||||
'combo': record.data.get_int('combo', -1),
|
||||
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
|
||||
'longrate': record.data.get_dict('stats').get_int('long_rate'),
|
||||
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
|
||||
"rank": rank,
|
||||
"status": status,
|
||||
"combo": record.data.get_int("combo", -1),
|
||||
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
|
||||
"longrate": record.data.get_dict("stats").get_int("long_rate"),
|
||||
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
|
||||
}
|
||||
|
||||
def __format_popn_record(self, record: Score) -> Dict[str, Any]:
|
||||
status = {
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: 'cf',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: 'df',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: 'sf',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: 'ec',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: 'cc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: 'dc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: 'sc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: 'cfc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: 'dfc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: 'sfc',
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: 'p',
|
||||
}.get(record.data.get_int('medal'), 'cf')
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: "cf",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: "df",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: "sf",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: "ec",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: "cc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: "dc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: "sc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: "cfc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: "dfc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: "sfc",
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: "p",
|
||||
}.get(record.data.get_int("medal"), "cf")
|
||||
|
||||
return {
|
||||
'status': status,
|
||||
'combo': record.data.get_int('combo', -1),
|
||||
"status": status,
|
||||
"combo": record.data.get_int("combo", -1),
|
||||
}
|
||||
|
||||
def __format_reflec_record(self, record: Score) -> Dict[str, Any]:
|
||||
status = {
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: 'np',
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: 'failed',
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: 'cleared',
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: 'hc',
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: 'shc',
|
||||
}.get(record.data.get_int('clear_type'), 'np')
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: "np",
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: "failed",
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: "cleared",
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: "hc",
|
||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: "shc",
|
||||
}.get(record.data.get_int("clear_type"), "np")
|
||||
halo = {
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: 'none',
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: 'ac',
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: 'fc',
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: 'fcaj',
|
||||
}.get(record.data.get_int('combo_type'), 'none')
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: "none",
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: "ac",
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: "fc",
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: "fcaj",
|
||||
}.get(record.data.get_int("combo_type"), "none")
|
||||
|
||||
return {
|
||||
'rate': record.data.get_int('achievement_rate'),
|
||||
'status': status,
|
||||
'halo': halo,
|
||||
'combo': record.data.get_int('combo', -1),
|
||||
'miss': record.data.get_int('miss_count', -1),
|
||||
"rate": record.data.get_int("achievement_rate"),
|
||||
"status": status,
|
||||
"halo": halo,
|
||||
"combo": record.data.get_int("combo", -1),
|
||||
"miss": record.data.get_int("miss_count", -1),
|
||||
}
|
||||
|
||||
def __format_sdvx_record(self, record: Score) -> Dict[str, Any]:
|
||||
status = {
|
||||
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: 'np',
|
||||
DBConstants.SDVX_CLEAR_TYPE_FAILED: 'failed',
|
||||
DBConstants.SDVX_CLEAR_TYPE_CLEAR: 'cleared',
|
||||
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: 'hc',
|
||||
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: 'uc',
|
||||
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: 'puc',
|
||||
}.get(record.data.get_int('clear_type'), 'np')
|
||||
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: "np",
|
||||
DBConstants.SDVX_CLEAR_TYPE_FAILED: "failed",
|
||||
DBConstants.SDVX_CLEAR_TYPE_CLEAR: "cleared",
|
||||
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: "hc",
|
||||
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: "uc",
|
||||
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: "puc",
|
||||
}.get(record.data.get_int("clear_type"), "np")
|
||||
rank = {
|
||||
DBConstants.SDVX_GRADE_NO_PLAY: 'E',
|
||||
DBConstants.SDVX_GRADE_D: 'D',
|
||||
DBConstants.SDVX_GRADE_C: 'C',
|
||||
DBConstants.SDVX_GRADE_B: 'B',
|
||||
DBConstants.SDVX_GRADE_A: 'A',
|
||||
DBConstants.SDVX_GRADE_A_PLUS: 'A+',
|
||||
DBConstants.SDVX_GRADE_AA: 'AA',
|
||||
DBConstants.SDVX_GRADE_AA_PLUS: 'AA+',
|
||||
DBConstants.SDVX_GRADE_AAA: 'AAA',
|
||||
DBConstants.SDVX_GRADE_AAA_PLUS: 'AAA+',
|
||||
DBConstants.SDVX_GRADE_S: 'S',
|
||||
}.get(record.data.get_int('grade'), 'E')
|
||||
DBConstants.SDVX_GRADE_NO_PLAY: "E",
|
||||
DBConstants.SDVX_GRADE_D: "D",
|
||||
DBConstants.SDVX_GRADE_C: "C",
|
||||
DBConstants.SDVX_GRADE_B: "B",
|
||||
DBConstants.SDVX_GRADE_A: "A",
|
||||
DBConstants.SDVX_GRADE_A_PLUS: "A+",
|
||||
DBConstants.SDVX_GRADE_AA: "AA",
|
||||
DBConstants.SDVX_GRADE_AA_PLUS: "AA+",
|
||||
DBConstants.SDVX_GRADE_AAA: "AAA",
|
||||
DBConstants.SDVX_GRADE_AAA_PLUS: "AAA+",
|
||||
DBConstants.SDVX_GRADE_S: "S",
|
||||
}.get(record.data.get_int("grade"), "E")
|
||||
|
||||
return {
|
||||
'status': status,
|
||||
'rank': rank,
|
||||
'combo': record.data.get_int('combo', -1),
|
||||
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
|
||||
'longrate': record.data.get_dict('stats').get_int('long_rate'),
|
||||
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
|
||||
"status": status,
|
||||
"rank": rank,
|
||||
"combo": record.data.get_int("combo", -1),
|
||||
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
|
||||
"longrate": record.data.get_dict("stats").get_int("long_rate"),
|
||||
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
|
||||
}
|
||||
|
||||
def __format_record(self, cardids: List[str], record: Score) -> Dict[str, Any]:
|
||||
base = {
|
||||
'cards': cardids,
|
||||
'song': str(record.id),
|
||||
'chart': str(record.chart),
|
||||
'points': record.points,
|
||||
'timestamp': record.timestamp,
|
||||
'updated': record.update,
|
||||
"cards": cardids,
|
||||
"song": str(record.id),
|
||||
"chart": str(record.chart),
|
||||
"points": record.points,
|
||||
"timestamp": record.timestamp,
|
||||
"updated": record.update,
|
||||
}
|
||||
|
||||
if self.game == GameConstants.DDR:
|
||||
@ -231,9 +230,11 @@ class RecordsObject(BaseObject):
|
||||
else:
|
||||
return self.version
|
||||
|
||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
since = params.get('since')
|
||||
until = params.get('until')
|
||||
def fetch_v1(
|
||||
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
since = params.get("since")
|
||||
until = params.get("until")
|
||||
|
||||
# Fetch the scores
|
||||
records: List[Tuple[UserID, Score]] = []
|
||||
@ -241,7 +242,9 @@ class RecordsObject(BaseObject):
|
||||
# Because of the way this query works, we can't apply since/until to it directly.
|
||||
# If we did, it would miss higher scores earned before since or after until, and
|
||||
# incorrectly report records.
|
||||
records.extend(self.data.local.music.get_all_records(self.game, self.music_version))
|
||||
records.extend(
|
||||
self.data.local.music.get_all_records(self.game, self.music_version)
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||
if len(ids) == 1:
|
||||
songid = int(ids[0])
|
||||
@ -249,14 +252,25 @@ class RecordsObject(BaseObject):
|
||||
else:
|
||||
songid = int(ids[0])
|
||||
chart = int(ids[1])
|
||||
records.extend(self.data.local.music.get_all_scores(self.game, self.music_version, songid=songid, songchart=chart, since=since, until=until))
|
||||
records.extend(
|
||||
self.data.local.music.get_all_scores(
|
||||
self.game,
|
||||
self.music_version,
|
||||
songid=songid,
|
||||
songchart=chart,
|
||||
since=since,
|
||||
until=until,
|
||||
)
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||
songid = int(ids[0])
|
||||
chart = int(ids[1])
|
||||
cardid = ids[2]
|
||||
userid = self.data.local.user.from_cardid(cardid)
|
||||
if userid is not None:
|
||||
score = self.data.local.music.get_score(self.game, self.music_version, userid, songid, chart)
|
||||
score = self.data.local.music.get_score(
|
||||
self.game, self.music_version, userid, songid, chart
|
||||
)
|
||||
if score is not None:
|
||||
records.append((userid, score))
|
||||
elif idtype == APIConstants.ID_TYPE_CARD:
|
||||
@ -270,9 +284,20 @@ class RecordsObject(BaseObject):
|
||||
continue
|
||||
users.add(userid)
|
||||
|
||||
records.extend([(userid, score) for score in self.data.local.music.get_scores(self.game, self.music_version, userid, since=since, until=until)])
|
||||
records.extend(
|
||||
[
|
||||
(userid, score)
|
||||
for score in self.data.local.music.get_scores(
|
||||
self.game,
|
||||
self.music_version,
|
||||
userid,
|
||||
since=since,
|
||||
until=until,
|
||||
)
|
||||
]
|
||||
)
|
||||
else:
|
||||
raise APIException('Invalid ID type!')
|
||||
raise APIException("Invalid ID type!")
|
||||
|
||||
# Now, fetch the users, and filter out scores belonging to orphaned users
|
||||
id_to_cards: Dict[UserID, List[str]] = {}
|
||||
|
@ -7,20 +7,21 @@ from bemani.data import Attempt, UserID
|
||||
|
||||
|
||||
class StatisticsObject(BaseObject):
|
||||
|
||||
def __format_statistics(self, stats: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return {
|
||||
'cards': [],
|
||||
'song': str(stats['id']),
|
||||
'chart': str(stats['chart']),
|
||||
'plays': stats.get('plays', -1),
|
||||
'clears': stats.get('clears', -1),
|
||||
'combos': stats.get('combos', -1),
|
||||
"cards": [],
|
||||
"song": str(stats["id"]),
|
||||
"chart": str(stats["chart"]),
|
||||
"plays": stats.get("plays", -1),
|
||||
"clears": stats.get("clears", -1),
|
||||
"combos": stats.get("combos", -1),
|
||||
}
|
||||
|
||||
def __format_user_statistics(self, cardids: List[str], stats: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def __format_user_statistics(
|
||||
self, cardids: List[str], stats: Dict[str, Any]
|
||||
) -> Dict[str, Any]:
|
||||
base = self.__format_statistics(stats)
|
||||
base['cards'] = cardids
|
||||
base["cards"] = cardids
|
||||
return base
|
||||
|
||||
@property
|
||||
@ -42,11 +43,20 @@ class StatisticsObject(BaseObject):
|
||||
}:
|
||||
return True
|
||||
if self.game == GameConstants.IIDX:
|
||||
return attempt.data.get_int('clear_status') != DBConstants.IIDX_CLEAR_STATUS_NO_PLAY
|
||||
return (
|
||||
attempt.data.get_int("clear_status")
|
||||
!= DBConstants.IIDX_CLEAR_STATUS_NO_PLAY
|
||||
)
|
||||
if self.game == GameConstants.REFLEC_BEAT:
|
||||
return attempt.data.get_int('clear_type') != DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
|
||||
return (
|
||||
attempt.data.get_int("clear_type")
|
||||
!= DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
|
||||
)
|
||||
if self.game == GameConstants.SDVX:
|
||||
return attempt.data.get_int('clear_type') != DBConstants.SDVX_CLEAR_TYPE_NO_PLAY
|
||||
return (
|
||||
attempt.data.get_int("clear_type")
|
||||
!= DBConstants.SDVX_CLEAR_TYPE_NO_PLAY
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
@ -55,29 +65,39 @@ class StatisticsObject(BaseObject):
|
||||
return False
|
||||
|
||||
if self.game == GameConstants.DDR:
|
||||
return attempt.data.get_int('rank') != DBConstants.DDR_RANK_E
|
||||
return attempt.data.get_int("rank") != DBConstants.DDR_RANK_E
|
||||
if self.game == GameConstants.IIDX:
|
||||
return attempt.data.get_int('clear_status') != DBConstants.IIDX_CLEAR_STATUS_FAILED
|
||||
return (
|
||||
attempt.data.get_int("clear_status")
|
||||
!= DBConstants.IIDX_CLEAR_STATUS_FAILED
|
||||
)
|
||||
if self.game == GameConstants.JUBEAT:
|
||||
return attempt.data.get_int('medal') != DBConstants.JUBEAT_PLAY_MEDAL_FAILED
|
||||
return attempt.data.get_int("medal") != DBConstants.JUBEAT_PLAY_MEDAL_FAILED
|
||||
if self.game == GameConstants.MUSECA:
|
||||
return attempt.data.get_int('clear_type') != DBConstants.MUSECA_CLEAR_TYPE_FAILED
|
||||
return (
|
||||
attempt.data.get_int("clear_type")
|
||||
!= DBConstants.MUSECA_CLEAR_TYPE_FAILED
|
||||
)
|
||||
if self.game == GameConstants.POPN_MUSIC:
|
||||
return attempt.data.get_int('medal') not in [
|
||||
return attempt.data.get_int("medal") not in [
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED,
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED,
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED,
|
||||
]
|
||||
if self.game == GameConstants.REFLEC_BEAT:
|
||||
return attempt.data.get_int('clear_type') != DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
|
||||
if self.game == GameConstants.SDVX:
|
||||
return (
|
||||
attempt.data.get_int('grade') != DBConstants.SDVX_GRADE_NO_PLAY and
|
||||
attempt.data.get_int('clear_type') not in [
|
||||
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY,
|
||||
DBConstants.SDVX_CLEAR_TYPE_FAILED,
|
||||
]
|
||||
attempt.data.get_int("clear_type")
|
||||
!= DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
|
||||
)
|
||||
if self.game == GameConstants.SDVX:
|
||||
return attempt.data.get_int(
|
||||
"grade"
|
||||
) != DBConstants.SDVX_GRADE_NO_PLAY and attempt.data.get_int(
|
||||
"clear_type"
|
||||
) not in [
|
||||
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY,
|
||||
DBConstants.SDVX_CLEAR_TYPE_FAILED,
|
||||
]
|
||||
|
||||
return False
|
||||
|
||||
@ -86,31 +106,37 @@ class StatisticsObject(BaseObject):
|
||||
return False
|
||||
|
||||
if self.game == GameConstants.DDR:
|
||||
return attempt.data.get_int('halo') != DBConstants.DDR_HALO_NONE
|
||||
return attempt.data.get_int("halo") != DBConstants.DDR_HALO_NONE
|
||||
if self.game == GameConstants.IIDX:
|
||||
return attempt.data.get_int('clear_status') == DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO
|
||||
return (
|
||||
attempt.data.get_int("clear_status")
|
||||
== DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO
|
||||
)
|
||||
if self.game == GameConstants.JUBEAT:
|
||||
return attempt.data.get_int('medal') in [
|
||||
return attempt.data.get_int("medal") in [
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO,
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT,
|
||||
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT,
|
||||
]
|
||||
if self.game == GameConstants.MUSECA:
|
||||
return attempt.data.get_int('clear_type') == DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO
|
||||
return (
|
||||
attempt.data.get_int("clear_type")
|
||||
== DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO
|
||||
)
|
||||
if self.game == GameConstants.POPN_MUSIC:
|
||||
return attempt.data.get_int('medal') in [
|
||||
return attempt.data.get_int("medal") in [
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO,
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO,
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO,
|
||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT,
|
||||
]
|
||||
if self.game == GameConstants.REFLEC_BEAT:
|
||||
return attempt.data.get_int('combo_type') in [
|
||||
return attempt.data.get_int("combo_type") in [
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO,
|
||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST,
|
||||
]
|
||||
if self.game == GameConstants.SDVX:
|
||||
return attempt.data.get_int('clear_type') in [
|
||||
return attempt.data.get_int("clear_type") in [
|
||||
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN,
|
||||
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN,
|
||||
]
|
||||
@ -125,29 +151,31 @@ class StatisticsObject(BaseObject):
|
||||
stats[attempt.id] = {}
|
||||
if attempt.chart not in stats[attempt.id]:
|
||||
stats[attempt.id][attempt.chart] = {
|
||||
'plays': 0,
|
||||
'clears': 0,
|
||||
'combos': 0,
|
||||
"plays": 0,
|
||||
"clears": 0,
|
||||
"combos": 0,
|
||||
}
|
||||
|
||||
if self.__is_play(attempt):
|
||||
stats[attempt.id][attempt.chart]['plays'] += 1
|
||||
stats[attempt.id][attempt.chart]["plays"] += 1
|
||||
if self.__is_clear(attempt):
|
||||
stats[attempt.id][attempt.chart]['clears'] += 1
|
||||
stats[attempt.id][attempt.chart]["clears"] += 1
|
||||
if self.__is_combo(attempt):
|
||||
stats[attempt.id][attempt.chart]['combos'] += 1
|
||||
stats[attempt.id][attempt.chart]["combos"] += 1
|
||||
|
||||
retval = []
|
||||
for songid in stats:
|
||||
for songchart in stats[songid]:
|
||||
stat = stats[songid][songchart]
|
||||
stat['id'] = songid
|
||||
stat['chart'] = songchart
|
||||
stat["id"] = songid
|
||||
stat["chart"] = songchart
|
||||
retval.append(self.__format_statistics(stat))
|
||||
|
||||
return retval
|
||||
|
||||
def __aggregate_local(self, cards: Dict[int, List[str]], attempts: List[Tuple[UserID, Attempt]]) -> List[Dict[str, Any]]:
|
||||
def __aggregate_local(
|
||||
self, cards: Dict[int, List[str]], attempts: List[Tuple[UserID, Attempt]]
|
||||
) -> List[Dict[str, Any]]:
|
||||
stats: Dict[UserID, Dict[int, Dict[int, Dict[str, int]]]] = {}
|
||||
|
||||
for (userid, attempt) in attempts:
|
||||
@ -157,36 +185,43 @@ class StatisticsObject(BaseObject):
|
||||
stats[userid][attempt.id] = {}
|
||||
if attempt.chart not in stats[userid][attempt.id]:
|
||||
stats[userid][attempt.id][attempt.chart] = {
|
||||
'plays': 0,
|
||||
'clears': 0,
|
||||
'combos': 0,
|
||||
"plays": 0,
|
||||
"clears": 0,
|
||||
"combos": 0,
|
||||
}
|
||||
|
||||
if self.__is_play(attempt):
|
||||
stats[userid][attempt.id][attempt.chart]['plays'] += 1
|
||||
stats[userid][attempt.id][attempt.chart]["plays"] += 1
|
||||
if self.__is_clear(attempt):
|
||||
stats[userid][attempt.id][attempt.chart]['clears'] += 1
|
||||
stats[userid][attempt.id][attempt.chart]["clears"] += 1
|
||||
if self.__is_combo(attempt):
|
||||
stats[userid][attempt.id][attempt.chart]['combos'] += 1
|
||||
stats[userid][attempt.id][attempt.chart]["combos"] += 1
|
||||
|
||||
retval = []
|
||||
for userid in stats:
|
||||
for songid in stats[userid]:
|
||||
for songchart in stats[userid][songid]:
|
||||
stat = stats[userid][songid][songchart]
|
||||
stat['id'] = songid
|
||||
stat['chart'] = songchart
|
||||
stat["id"] = songid
|
||||
stat["chart"] = songchart
|
||||
retval.append(self.__format_user_statistics(cards[userid], stat))
|
||||
|
||||
return retval
|
||||
|
||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
def fetch_v1(
|
||||
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
retval: List[Dict[str, Any]] = []
|
||||
|
||||
# Fetch the attempts
|
||||
if idtype == APIConstants.ID_TYPE_SERVER:
|
||||
retval = self.__aggregate_global(
|
||||
[attempt[1] for attempt in self.data.local.music.get_all_attempts(self.game, self.music_version)]
|
||||
[
|
||||
attempt[1]
|
||||
for attempt in self.data.local.music.get_all_attempts(
|
||||
self.game, self.music_version
|
||||
)
|
||||
]
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||
if len(ids) == 1:
|
||||
@ -196,7 +231,12 @@ class StatisticsObject(BaseObject):
|
||||
songid = int(ids[0])
|
||||
chart = int(ids[1])
|
||||
retval = self.__aggregate_global(
|
||||
[attempt[1] for attempt in self.data.local.music.get_all_attempts(self.game, self.music_version, songid=songid, songchart=chart)]
|
||||
[
|
||||
attempt[1]
|
||||
for attempt in self.data.local.music.get_all_attempts(
|
||||
self.game, self.music_version, songid=songid, songchart=chart
|
||||
)
|
||||
]
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||
songid = int(ids[0])
|
||||
@ -206,7 +246,13 @@ class StatisticsObject(BaseObject):
|
||||
if userid is not None:
|
||||
retval = self.__aggregate_local(
|
||||
{userid: self.data.local.user.get_cards(userid)},
|
||||
self.data.local.music.get_all_attempts(self.game, self.music_version, songid=songid, songchart=chart, userid=userid)
|
||||
self.data.local.music.get_all_attempts(
|
||||
self.game,
|
||||
self.music_version,
|
||||
songid=songid,
|
||||
songchart=chart,
|
||||
userid=userid,
|
||||
),
|
||||
)
|
||||
elif idtype == APIConstants.ID_TYPE_CARD:
|
||||
id_to_cards: Dict[int, List[str]] = {}
|
||||
@ -221,10 +267,12 @@ class StatisticsObject(BaseObject):
|
||||
|
||||
id_to_cards[userid] = self.data.local.user.get_cards(userid)
|
||||
attempts.extend(
|
||||
self.data.local.music.get_all_attempts(self.game, self.music_version, userid=userid)
|
||||
self.data.local.music.get_all_attempts(
|
||||
self.game, self.music_version, userid=userid
|
||||
)
|
||||
)
|
||||
retval = self.__aggregate_local(id_to_cards, attempts)
|
||||
else:
|
||||
raise APIException('Invalid ID type!')
|
||||
raise APIException("Invalid ID type!")
|
||||
|
||||
return retval
|
||||
|
@ -3,7 +3,15 @@ import traceback
|
||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
||||
from typing_extensions import Final
|
||||
|
||||
from bemani.common import Model, ValidatedDict, Profile, PlayStatistics, GameConstants, RegionConstants, Time
|
||||
from bemani.common import (
|
||||
Model,
|
||||
ValidatedDict,
|
||||
Profile,
|
||||
PlayStatistics,
|
||||
GameConstants,
|
||||
RegionConstants,
|
||||
Time,
|
||||
)
|
||||
from bemani.data import Config, Data, Arcade, Machine, UserID, RemoteUser
|
||||
|
||||
|
||||
@ -15,6 +23,7 @@ class Status:
|
||||
"""
|
||||
List of statuses we return to the game for various reasons.
|
||||
"""
|
||||
|
||||
SUCCESS: Final[int] = 0
|
||||
NO_PROFILE: Final[int] = 109
|
||||
NOT_ALLOWED: Final[int] = 110
|
||||
@ -41,7 +50,7 @@ class Factory(ABC):
|
||||
with Base, using Base.register(). Factories specify the game code that
|
||||
they support, which Base will use when routing requests.
|
||||
"""
|
||||
raise NotImplementedError('Override this in subclass!')
|
||||
raise NotImplementedError("Override this in subclass!")
|
||||
|
||||
@classmethod
|
||||
def run_scheduled_work(cls, data: Data, config: Config) -> None:
|
||||
@ -59,10 +68,10 @@ class Factory(ABC):
|
||||
stack = traceback.format_exc()
|
||||
print(stack)
|
||||
data.local.network.put_event(
|
||||
'exception',
|
||||
"exception",
|
||||
{
|
||||
'service': 'scheduler',
|
||||
'traceback': stack,
|
||||
"service": "scheduler",
|
||||
"traceback": stack,
|
||||
},
|
||||
)
|
||||
for event in events:
|
||||
@ -88,7 +97,13 @@ class Factory(ABC):
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']:
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional["Base"]:
|
||||
"""
|
||||
Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet.
|
||||
|
||||
@ -106,7 +121,7 @@ class Factory(ABC):
|
||||
A subclass of Base that hopefully has a handle_<call>_request method on it, for the particular
|
||||
call that Dispatch wants to resolve, or None if we can't look up a game.
|
||||
"""
|
||||
raise NotImplementedError('Override this in subclass!')
|
||||
raise NotImplementedError("Override this in subclass!")
|
||||
|
||||
|
||||
class Base(ABC):
|
||||
@ -172,7 +187,13 @@ class Base(ABC):
|
||||
self.model = model
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional['Base']:
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional["Base"]:
|
||||
"""
|
||||
Given a modelstring and an optional parent model, return an instantiated game class that can handle a packet.
|
||||
|
||||
@ -200,7 +221,9 @@ class Base(ABC):
|
||||
return Base(data, config, model)
|
||||
else:
|
||||
# Return the registered module providing this game
|
||||
return cls.__registered_games[model.gamecode].create(data, config, model, parentmodel=parentmodel)
|
||||
return cls.__registered_games[model.gamecode].create(
|
||||
data, config, model, parentmodel=parentmodel
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def register(cls, gamecode: str, handler: Type[Factory]) -> None:
|
||||
@ -216,7 +239,9 @@ class Base(ABC):
|
||||
cls.__registered_handlers.add(handler)
|
||||
|
||||
@classmethod
|
||||
def run_scheduled_work(cls, data: Data, config: Config) -> List[Tuple[str, Dict[str, Any]]]:
|
||||
def run_scheduled_work(
|
||||
cls, data: Data, config: Config
|
||||
) -> List[Tuple[str, Dict[str, Any]]]:
|
||||
"""
|
||||
Run any out-of-band scheduled work that is applicable to this game.
|
||||
"""
|
||||
@ -267,7 +292,10 @@ class Base(ABC):
|
||||
Returns:
|
||||
True if the profile exists, False if not.
|
||||
"""
|
||||
return self.data.local.user.get_profile(self.game, self.version, userid) is not None
|
||||
return (
|
||||
self.data.local.user.get_profile(self.game, self.version, userid)
|
||||
is not None
|
||||
)
|
||||
|
||||
def get_profile(self, userid: UserID) -> Optional[Profile]:
|
||||
"""
|
||||
@ -321,9 +349,16 @@ class Base(ABC):
|
||||
or an empty dictionary if nothing was found.
|
||||
"""
|
||||
userids = list(set(userids))
|
||||
profiles = self.data.remote.user.get_any_profiles(self.game, self.version, userids)
|
||||
profiles = self.data.remote.user.get_any_profiles(
|
||||
self.game, self.version, userids
|
||||
)
|
||||
return [
|
||||
(userid, profile if profile is not None else Profile(self.game, self.version, "", 0))
|
||||
(
|
||||
userid,
|
||||
profile
|
||||
if profile is not None
|
||||
else Profile(self.game, self.version, "", 0),
|
||||
)
|
||||
for (userid, profile) in profiles
|
||||
]
|
||||
|
||||
@ -336,10 +371,12 @@ class Base(ABC):
|
||||
profile - A dictionary that should be looked up later using get_profile.
|
||||
"""
|
||||
if RemoteUser.is_remote(userid):
|
||||
raise Exception('Trying to save a remote profile locally!')
|
||||
raise Exception("Trying to save a remote profile locally!")
|
||||
self.data.local.user.put_profile(self.game, self.version, userid, profile)
|
||||
|
||||
def update_play_statistics(self, userid: UserID, stats: Optional[PlayStatistics] = None) -> None:
|
||||
def update_play_statistics(
|
||||
self, userid: UserID, stats: Optional[PlayStatistics] = None
|
||||
) -> None:
|
||||
"""
|
||||
Given a user ID, calculate new play statistics.
|
||||
|
||||
@ -351,59 +388,65 @@ class Base(ABC):
|
||||
stats - A play statistics object we should store extra data from.
|
||||
"""
|
||||
if RemoteUser.is_remote(userid):
|
||||
raise Exception('Trying to save remote statistics locally!')
|
||||
raise Exception("Trying to save remote statistics locally!")
|
||||
|
||||
# We store the play statistics in a series-wide settings blob so its available
|
||||
# across all game versions, since it isn't game-specific.
|
||||
settings = self.data.local.game.get_settings(self.game, userid) or ValidatedDict({})
|
||||
settings = self.data.local.game.get_settings(
|
||||
self.game, userid
|
||||
) or ValidatedDict({})
|
||||
|
||||
if stats is not None:
|
||||
for key in stats:
|
||||
# Make sure we don't override anything we manage here
|
||||
if key in {
|
||||
'total_plays',
|
||||
'today_plays',
|
||||
'total_days',
|
||||
'first_play_timestamp',
|
||||
'last_play_timestamp',
|
||||
'last_play_date',
|
||||
'consecutive_days',
|
||||
"total_plays",
|
||||
"today_plays",
|
||||
"total_days",
|
||||
"first_play_timestamp",
|
||||
"last_play_timestamp",
|
||||
"last_play_date",
|
||||
"consecutive_days",
|
||||
}:
|
||||
continue
|
||||
# Safe to copy over
|
||||
settings[key] = stats[key]
|
||||
|
||||
settings.replace_int('total_plays', settings.get_int('total_plays') + 1)
|
||||
settings.replace_int('first_play_timestamp', settings.get_int('first_play_timestamp', Time.now()))
|
||||
settings.replace_int('last_play_timestamp', Time.now())
|
||||
settings.replace_int("total_plays", settings.get_int("total_plays") + 1)
|
||||
settings.replace_int(
|
||||
"first_play_timestamp", settings.get_int("first_play_timestamp", Time.now())
|
||||
)
|
||||
settings.replace_int("last_play_timestamp", Time.now())
|
||||
|
||||
last_play_date = settings.get_int_array('last_play_date', 3)
|
||||
last_play_date = settings.get_int_array("last_play_date", 3)
|
||||
today_play_date = Time.todays_date()
|
||||
yesterday_play_date = Time.yesterdays_date()
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
last_play_date[0] == today_play_date[0]
|
||||
and last_play_date[1] == today_play_date[1]
|
||||
and last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
# We already played today, add one.
|
||||
settings.replace_int('today_plays', settings.get_int('today_plays') + 1)
|
||||
settings.replace_int("today_plays", settings.get_int("today_plays") + 1)
|
||||
else:
|
||||
# We played on a new day, so count total days up.
|
||||
settings.replace_int('total_days', settings.get_int('total_days') + 1)
|
||||
settings.replace_int("total_days", settings.get_int("total_days") + 1)
|
||||
|
||||
# We played only once today (the play we are saving).
|
||||
settings.replace_int('today_plays', 1)
|
||||
settings.replace_int("today_plays", 1)
|
||||
if (
|
||||
last_play_date[0] == yesterday_play_date[0] and
|
||||
last_play_date[1] == yesterday_play_date[1] and
|
||||
last_play_date[2] == yesterday_play_date[2]
|
||||
last_play_date[0] == yesterday_play_date[0]
|
||||
and last_play_date[1] == yesterday_play_date[1]
|
||||
and last_play_date[2] == yesterday_play_date[2]
|
||||
):
|
||||
# We played yesterday, add one to consecutive days
|
||||
settings.replace_int('consecutive_days', settings.get_int('consecutive_days') + 1)
|
||||
settings.replace_int(
|
||||
"consecutive_days", settings.get_int("consecutive_days") + 1
|
||||
)
|
||||
else:
|
||||
# We haven't played yesterday, so we have only one consecutive day.
|
||||
settings.replace_int('consecutive_days', 1)
|
||||
settings.replace_int_array('last_play_date', 3, today_play_date)
|
||||
settings.replace_int("consecutive_days", 1)
|
||||
settings.replace_int_array("last_play_date", 3, today_play_date)
|
||||
|
||||
# Save back
|
||||
self.data.local.game.put_settings(self.game, userid, settings)
|
||||
@ -442,9 +485,13 @@ class Base(ABC):
|
||||
def get_machine_region(self) -> int:
|
||||
arcade = self.get_arcade()
|
||||
if arcade is None:
|
||||
return RegionConstants.db_to_game_region(self.requires_extended_regions, self.config.server.region)
|
||||
return RegionConstants.db_to_game_region(
|
||||
self.requires_extended_regions, self.config.server.region
|
||||
)
|
||||
else:
|
||||
return RegionConstants.db_to_game_region(self.requires_extended_regions, arcade.region)
|
||||
return RegionConstants.db_to_game_region(
|
||||
self.requires_extended_regions, arcade.region
|
||||
)
|
||||
|
||||
def get_game_config(self) -> ValidatedDict:
|
||||
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||
@ -452,7 +499,9 @@ class Base(ABC):
|
||||
# If this machine belongs to an arcade, use its settings. If the settings aren't present,
|
||||
# default to the game's defaults.
|
||||
if machine.arcade is not None:
|
||||
settings = self.data.local.machine.get_settings(machine.arcade, self.game, self.version, 'game_config')
|
||||
settings = self.data.local.machine.get_settings(
|
||||
machine.arcade, self.game, self.version, "game_config"
|
||||
)
|
||||
if settings is None:
|
||||
settings = ValidatedDict()
|
||||
return settings
|
||||
@ -460,7 +509,12 @@ class Base(ABC):
|
||||
# If this machine does not belong to an arcade, use the server-wide settings. If the settings
|
||||
# aren't present, default ot the game's default.
|
||||
else:
|
||||
settings = self.data.local.machine.get_settings(self.data.local.machine.DEFAULT_SETTINGS_ARCADE, self.game, self.version, 'game_config')
|
||||
settings = self.data.local.machine.get_settings(
|
||||
self.data.local.machine.DEFAULT_SETTINGS_ARCADE,
|
||||
self.game,
|
||||
self.version,
|
||||
"game_config",
|
||||
)
|
||||
if settings is None:
|
||||
settings = ValidatedDict()
|
||||
return settings
|
||||
@ -511,38 +565,38 @@ class Base(ABC):
|
||||
)
|
||||
|
||||
# Calculate whether we are on our first play of the day or not.
|
||||
last_play_date = settings.get_int_array('last_play_date', 3)
|
||||
last_play_date = settings.get_int_array("last_play_date", 3)
|
||||
if (
|
||||
last_play_date[0] == today_play_date[0] and
|
||||
last_play_date[1] == today_play_date[1] and
|
||||
last_play_date[2] == today_play_date[2]
|
||||
last_play_date[0] == today_play_date[0]
|
||||
and last_play_date[1] == today_play_date[1]
|
||||
and last_play_date[2] == today_play_date[2]
|
||||
):
|
||||
# We last played today, so the total days and today plays are accurate
|
||||
# as stored.
|
||||
today_count = settings.get_int('today_plays', 0)
|
||||
total_days = settings.get_int('total_days', 1)
|
||||
consecutive_days = settings.get_int('consecutive_days', 1)
|
||||
today_count = settings.get_int("today_plays", 0)
|
||||
total_days = settings.get_int("total_days", 1)
|
||||
consecutive_days = settings.get_int("consecutive_days", 1)
|
||||
else:
|
||||
if (
|
||||
last_play_date[0] != 0 and
|
||||
last_play_date[1] != 0 and
|
||||
last_play_date[2] != 0
|
||||
last_play_date[0] != 0
|
||||
and last_play_date[1] != 0
|
||||
and last_play_date[2] != 0
|
||||
):
|
||||
# We've played before but not today, so the total days is
|
||||
# the stored count plus today.
|
||||
total_days = settings.get_int('total_days') + 1
|
||||
total_days = settings.get_int("total_days") + 1
|
||||
else:
|
||||
# We've never played before, so the total days is just 1.
|
||||
total_days = 1
|
||||
|
||||
if (
|
||||
last_play_date[0] == yesterday_play_date[0] and
|
||||
last_play_date[1] == yesterday_play_date[1] and
|
||||
last_play_date[2] == yesterday_play_date[2]
|
||||
last_play_date[0] == yesterday_play_date[0]
|
||||
and last_play_date[1] == yesterday_play_date[1]
|
||||
and last_play_date[2] == yesterday_play_date[2]
|
||||
):
|
||||
# We've played before, and it was yesterday, so today is the
|
||||
# next consecutive day. So add the current value and today.
|
||||
consecutive_days = settings.get_int('consecutive_days') + 1
|
||||
consecutive_days = settings.get_int("consecutive_days") + 1
|
||||
else:
|
||||
# This is the first consecutive day, we've either never played
|
||||
# or we played a bunch but in the past before yesterday.
|
||||
@ -553,25 +607,27 @@ class Base(ABC):
|
||||
|
||||
# Grab any extra settings that a game may have stored here.
|
||||
extra_settings: Dict[str, Any] = {
|
||||
key: value for (key, value) in settings.items()
|
||||
if key not in {
|
||||
'total_plays',
|
||||
'today_plays',
|
||||
'total_days',
|
||||
'first_play_timestamp',
|
||||
'last_play_timestamp',
|
||||
'last_play_date',
|
||||
'consecutive_days',
|
||||
key: value
|
||||
for (key, value) in settings.items()
|
||||
if key
|
||||
not in {
|
||||
"total_plays",
|
||||
"today_plays",
|
||||
"total_days",
|
||||
"first_play_timestamp",
|
||||
"last_play_timestamp",
|
||||
"last_play_date",
|
||||
"consecutive_days",
|
||||
}
|
||||
}
|
||||
|
||||
return PlayStatistics(
|
||||
self.game,
|
||||
settings.get_int('total_plays') + 1,
|
||||
settings.get_int("total_plays") + 1,
|
||||
today_count + 1,
|
||||
total_days,
|
||||
consecutive_days,
|
||||
settings.get_int('first_play_timestamp', Time.now()),
|
||||
settings.get_int('last_play_timestamp', Time.now()),
|
||||
settings.get_int("first_play_timestamp", Time.now()),
|
||||
settings.get_int("last_play_timestamp", Time.now()),
|
||||
extra_settings,
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ class BishiBashiBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
game: GameConstants = GameConstants.BISHI_BASHI
|
||||
|
||||
def previous_version(self) -> Optional['BishiBashiBase']:
|
||||
def previous_version(self) -> Optional["BishiBashiBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
|
@ -1,6 +1,7 @@
|
||||
# vim: set fileencoding=utf-8
|
||||
import binascii
|
||||
import base64
|
||||
|
||||
try:
|
||||
# Python <= 3.9
|
||||
from collections import Iterable
|
||||
@ -30,58 +31,58 @@ class TheStarBishiBashi(
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Force Unlock All Characters',
|
||||
'tip': 'Force unlock all characters on select screen.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_characters',
|
||||
"name": "Force Unlock All Characters",
|
||||
"tip": "Force unlock all characters on select screen.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_characters",
|
||||
},
|
||||
{
|
||||
'name': 'Unlock Non-Gacha Characters',
|
||||
'tip': 'Unlock characters that require playing a different game to unlock.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_eamuse_characters',
|
||||
"name": "Unlock Non-Gacha Characters",
|
||||
"tip": "Unlock characters that require playing a different game to unlock.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_eamuse_characters",
|
||||
},
|
||||
{
|
||||
'name': 'Enable DLC levels',
|
||||
'tip': 'Enable extra DLC levels on newer cabinets.',
|
||||
'category': 'game_config',
|
||||
'setting': 'enable_dlc_levels',
|
||||
"name": "Enable DLC levels",
|
||||
"tip": "Enable extra DLC levels on newer cabinets.",
|
||||
"category": "game_config",
|
||||
"setting": "enable_dlc_levels",
|
||||
},
|
||||
],
|
||||
'strs': [
|
||||
"strs": [
|
||||
{
|
||||
'name': 'Scrolling Announcement',
|
||||
'tip': 'An announcement that scrolls by in attract mode.',
|
||||
'category': 'game_config',
|
||||
'setting': 'big_announcement',
|
||||
"name": "Scrolling Announcement",
|
||||
"tip": "An announcement that scrolls by in attract mode.",
|
||||
"category": "game_config",
|
||||
"setting": "big_announcement",
|
||||
},
|
||||
],
|
||||
'longstrs': [
|
||||
"longstrs": [
|
||||
{
|
||||
'name': 'Bulletin Board Announcement',
|
||||
'tip': 'An announcement displayed on a bulletin board in attract mode.',
|
||||
'category': 'game_config',
|
||||
'setting': 'bb_announcement',
|
||||
"name": "Bulletin Board Announcement",
|
||||
"tip": "An announcement displayed on a bulletin board in attract mode.",
|
||||
"category": "game_config",
|
||||
"setting": "bb_announcement",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def __update_shop_name(self, profiledata: bytes) -> None:
|
||||
# Figure out the profile type
|
||||
csvs = profiledata.split(b',')
|
||||
csvs = profiledata.split(b",")
|
||||
if len(csvs) < 2:
|
||||
# Not long enough to care about
|
||||
return
|
||||
datatype = csvs[1].decode('ascii')
|
||||
if datatype != 'IBBDAT00':
|
||||
datatype = csvs[1].decode("ascii")
|
||||
if datatype != "IBBDAT00":
|
||||
# Not the right profile type requested
|
||||
return
|
||||
|
||||
# Grab the shop name
|
||||
try:
|
||||
shopname = csvs[30].decode('shift-jis')
|
||||
shopname = csvs[30].decode("shift-jis")
|
||||
except Exception:
|
||||
return
|
||||
self.update_machine_name(shopname)
|
||||
@ -98,7 +99,9 @@ class TheStarBishiBashi(
|
||||
data = data.replace(";", "#;")
|
||||
return data
|
||||
|
||||
def __generate_setting(self, key: str, values: Union[int, str, Sequence[int], Sequence[str]]) -> str:
|
||||
def __generate_setting(
|
||||
self, key: str, values: Union[int, str, Sequence[int], Sequence[str]]
|
||||
) -> str:
|
||||
if isinstance(values, Iterable) and not isinstance(values, str):
|
||||
values = ",".join(self.__escape_string(x) for x in values)
|
||||
else:
|
||||
@ -108,18 +111,18 @@ class TheStarBishiBashi(
|
||||
|
||||
def handle_system_getmaster_request(self, request: Node) -> Node:
|
||||
# See if we can grab the request
|
||||
data = request.child('data')
|
||||
data = request.child("data")
|
||||
if not data:
|
||||
root = Node.void('system')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root = Node.void("system")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
return root
|
||||
|
||||
# Figure out what type of messsage this is
|
||||
reqtype = data.child_value('datatype')
|
||||
reqkey = data.child_value('datakey')
|
||||
reqtype = data.child_value("datatype")
|
||||
reqkey = data.child_value("datakey")
|
||||
|
||||
# System message
|
||||
root = Node.void('system')
|
||||
root = Node.void("system")
|
||||
|
||||
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
||||
# Settings that we can tweak from the server.
|
||||
@ -179,40 +182,51 @@ class TheStarBishiBashi(
|
||||
settings: Dict[str, Union[int, str, Sequence[int], Sequence[str]]] = {}
|
||||
|
||||
game_config = self.get_game_config()
|
||||
enable_dlc_levels = game_config.get_bool('enable_dlc_levels')
|
||||
enable_dlc_levels = game_config.get_bool("enable_dlc_levels")
|
||||
if enable_dlc_levels:
|
||||
settings['MAL'] = 1
|
||||
force_unlock_characters = game_config.get_bool('force_unlock_eamuse_characters')
|
||||
settings["MAL"] = 1
|
||||
force_unlock_characters = game_config.get_bool(
|
||||
"force_unlock_eamuse_characters"
|
||||
)
|
||||
if force_unlock_characters:
|
||||
settings['ALL'] = 1
|
||||
scrolling_message = game_config.get_str('big_announcement')
|
||||
settings["ALL"] = 1
|
||||
scrolling_message = game_config.get_str("big_announcement")
|
||||
if scrolling_message:
|
||||
settings['CM'] = scrolling_message
|
||||
bb_message = game_config.get_str('bb_announcement')
|
||||
settings["CM"] = scrolling_message
|
||||
bb_message = game_config.get_str("bb_announcement")
|
||||
if bb_message:
|
||||
settings['IM'] = bb_message
|
||||
settings["IM"] = bb_message
|
||||
|
||||
# Generate system message
|
||||
settings_str = ";".join(self.__generate_setting(key, vals) for key, vals in settings.items())
|
||||
settings_str = ";".join(
|
||||
self.__generate_setting(key, vals) for key, vals in settings.items()
|
||||
)
|
||||
|
||||
# Send it to the client, making sure to inform the client that it was valid.
|
||||
root.add_child(Node.string('strdata1', base64.b64encode(settings_str.encode('ascii')).decode('ascii')))
|
||||
root.add_child(Node.string('strdata2', ""))
|
||||
root.add_child(Node.u64('updatedate', Time.now() * 1000))
|
||||
root.add_child(Node.s32('result', 1))
|
||||
root.add_child(
|
||||
Node.string(
|
||||
"strdata1",
|
||||
base64.b64encode(settings_str.encode("ascii")).decode("ascii"),
|
||||
)
|
||||
)
|
||||
root.add_child(Node.string("strdata2", ""))
|
||||
root.add_child(Node.u64("updatedate", Time.now() * 1000))
|
||||
root.add_child(Node.s32("result", 1))
|
||||
else:
|
||||
# Unknown message.
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root.add_child(Node.s32("result", 0))
|
||||
|
||||
return root
|
||||
|
||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||
# Look up user by refid
|
||||
refid = request.child_value('data/eaid')
|
||||
refid = request.child_value("data/eaid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(
|
||||
Node.s32("result", 1)
|
||||
) # Unclear if this is the right thing to do here.
|
||||
return root
|
||||
|
||||
# Extract new profile info from old profile
|
||||
@ -227,14 +241,14 @@ class TheStarBishiBashi(
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
# Return success!
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
return root
|
||||
|
||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||
# Look up user by refid
|
||||
refid = request.child_value('data/eaid')
|
||||
profiletype = request.child_value('data/recv_csv').split(',')[0]
|
||||
refid = request.child_value("data/eaid")
|
||||
profiletype = request.child_value("data/recv_csv").split(",")[0]
|
||||
profile = None
|
||||
userid = None
|
||||
if refid is not None:
|
||||
@ -244,40 +258,44 @@ class TheStarBishiBashi(
|
||||
if profile is not None:
|
||||
return self.format_profile(userid, profiletype, profile)
|
||||
else:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(
|
||||
Node.s32("result", 1)
|
||||
) # Unclear if this is the right thing to do here.
|
||||
return root
|
||||
|
||||
def format_profile(self, userid: UserID, profiletype: str, profile: Profile) -> Node:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
player = Node.void('player')
|
||||
def format_profile(
|
||||
self, userid: UserID, profiletype: str, profile: Profile
|
||||
) -> Node:
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
player = Node.void("player")
|
||||
root.add_child(player)
|
||||
records = 0
|
||||
|
||||
for i in range(len(profile['strdatas'])):
|
||||
strdata = profile['strdatas'][i]
|
||||
bindata = profile['bindatas'][i]
|
||||
for i in range(len(profile["strdatas"])):
|
||||
strdata = profile["strdatas"][i]
|
||||
bindata = profile["bindatas"][i]
|
||||
|
||||
# Figure out the profile type
|
||||
csvs = strdata.split(b',')
|
||||
csvs = strdata.split(b",")
|
||||
if len(csvs) < 2:
|
||||
# Not long enough to care about
|
||||
continue
|
||||
datatype = csvs[1].decode('ascii')
|
||||
datatype = csvs[1].decode("ascii")
|
||||
if datatype != profiletype:
|
||||
# Not the right profile type requested
|
||||
continue
|
||||
|
||||
game_config = self.get_game_config()
|
||||
force_unlock_characters = game_config.get_bool('force_unlock_characters')
|
||||
force_unlock_characters = game_config.get_bool("force_unlock_characters")
|
||||
if force_unlock_characters:
|
||||
csvs[11] = b'3ffffffffffff'
|
||||
csvs[11] = b"3ffffffffffff"
|
||||
else:
|
||||
# Reward characters based on playing other games on the network
|
||||
hexdata = csvs[11].decode('ascii')
|
||||
hexdata = csvs[11].decode("ascii")
|
||||
while (len(hexdata) & 1) != 0:
|
||||
hexdata = '0' + hexdata
|
||||
hexdata = "0" + hexdata
|
||||
unlock_bits = [b for b in binascii.unhexlify(hexdata)]
|
||||
while len(unlock_bits) < 7:
|
||||
unlock_bits.insert(0, 0)
|
||||
@ -309,32 +327,34 @@ class TheStarBishiBashi(
|
||||
|
||||
# Reconstruct table
|
||||
unlock_bits = unlock_bits[::-1]
|
||||
csvs[11] = ''.join([f'{x:02x}' for x in unlock_bits]).encode('ascii')
|
||||
csvs[11] = "".join([f"{x:02x}" for x in unlock_bits]).encode("ascii")
|
||||
|
||||
# This is a valid profile node for this type, lets return only the profile values
|
||||
strdata = b','.join(csvs[2:])
|
||||
record = Node.void('record')
|
||||
strdata = b",".join(csvs[2:])
|
||||
record = Node.void("record")
|
||||
player.add_child(record)
|
||||
d = Node.string('d', base64.b64encode(strdata).decode('ascii'))
|
||||
d = Node.string("d", base64.b64encode(strdata).decode("ascii"))
|
||||
record.add_child(d)
|
||||
d.add_child(Node.string('bin1', base64.b64encode(bindata).decode('ascii')))
|
||||
d.add_child(Node.string("bin1", base64.b64encode(bindata).decode("ascii")))
|
||||
|
||||
# Remember that we had this record
|
||||
records = records + 1
|
||||
|
||||
player.add_child(Node.u32('record_num', records))
|
||||
player.add_child(Node.u32("record_num", records))
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool
|
||||
) -> Profile:
|
||||
# Profile save request, data values are base64 encoded.
|
||||
# d is a CSV, and bin1 is binary data.
|
||||
newprofile = oldprofile.clone()
|
||||
strdatas: List[bytes] = []
|
||||
bindatas: List[bytes] = []
|
||||
|
||||
record = request.child('data/record')
|
||||
record = request.child("data/record")
|
||||
for node in record.children:
|
||||
if node.name != 'd':
|
||||
if node.name != "d":
|
||||
continue
|
||||
|
||||
profile = base64.b64decode(node.value)
|
||||
@ -344,10 +364,10 @@ class TheStarBishiBashi(
|
||||
if is_new:
|
||||
self.__update_shop_name(profile)
|
||||
strdatas.append(profile)
|
||||
bindatas.append(base64.b64decode(node.child_value('bin1')))
|
||||
bindatas.append(base64.b64decode(node.child_value("bin1")))
|
||||
|
||||
newprofile['strdatas'] = strdatas
|
||||
newprofile['bindatas'] = bindatas
|
||||
newprofile["strdatas"] = strdatas
|
||||
newprofile["bindatas"] = bindatas
|
||||
|
||||
# Keep track of play statistics across all versions
|
||||
self.update_play_statistics(userid)
|
||||
|
@ -14,13 +14,19 @@ class BishiBashiFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['IBB']:
|
||||
for gamecode in ["IBB"]:
|
||||
Base.register(gamecode, BishiBashiFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
|
||||
if model.gamecode == 'IBB':
|
||||
if model.gamecode == "IBB":
|
||||
return TheStarBishiBashi(data, config, model)
|
||||
|
||||
# Unknown game version
|
||||
|
@ -19,14 +19,14 @@ class CardManagerHandler(Base):
|
||||
# Given a cardid, look up the dataid/refid (same thing in this system).
|
||||
# If the card doesn't exist or isn't allowed, return a status specifying this
|
||||
# instead of the results of the dataid/refid lookup.
|
||||
cardid = request.attribute('cardid')
|
||||
modelstring = request.attribute('model')
|
||||
cardid = request.attribute("cardid")
|
||||
modelstring = request.attribute("model")
|
||||
userid = self.data.local.user.from_cardid(cardid)
|
||||
|
||||
if userid is None:
|
||||
# This user doesn't exist, force system to create new account
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('status', str(Status.NOT_REGISTERED))
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("status", str(Status.NOT_REGISTERED))
|
||||
return root
|
||||
|
||||
# Special handling for looking up whether the previous game's profile existed. If we
|
||||
@ -43,77 +43,82 @@ class CardManagerHandler(Base):
|
||||
|
||||
refid = self.data.local.user.get_refid(self.game, self.version, userid)
|
||||
paseli_enabled = self.supports_paseli and self.config.paseli.enabled
|
||||
newflag = self.data.remote.user.get_any_profile(self.game, self.version, userid) is None
|
||||
newflag = (
|
||||
self.data.remote.user.get_any_profile(self.game, self.version, userid)
|
||||
is None
|
||||
)
|
||||
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('refid', refid)
|
||||
root.set_attribute('dataid', refid)
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("refid", refid)
|
||||
root.set_attribute("dataid", refid)
|
||||
|
||||
# Unsure what this does, but it appears not to matter so we set it to my best guess.
|
||||
root.set_attribute('newflag', '1' if newflag else '0')
|
||||
root.set_attribute("newflag", "1" if newflag else "0")
|
||||
|
||||
# Whether we've bound a profile to this refid/dataid or not. This includes current profiles and any
|
||||
# older game profiles that might exist that we should do a conversion from.
|
||||
root.set_attribute('binded', '1' if bound else '0')
|
||||
root.set_attribute("binded", "1" if bound else "0")
|
||||
|
||||
# Whether this version of the profile is expired (was converted to newer version). We support forwards
|
||||
# and backwards compatibility so some games will always set this to 0.
|
||||
root.set_attribute('expired', '1' if expired else '0')
|
||||
root.set_attribute("expired", "1" if expired else "0")
|
||||
|
||||
# Whether to allow paseli, as enabled by the operator and arcade owner.
|
||||
root.set_attribute('ecflag', '1' if paseli_enabled else '0')
|
||||
root.set_attribute("ecflag", "1" if paseli_enabled else "0")
|
||||
|
||||
# I have absolutely no idea what these do.
|
||||
root.set_attribute('useridflag', '1')
|
||||
root.set_attribute('extidflag', '1')
|
||||
root.set_attribute("useridflag", "1")
|
||||
root.set_attribute("extidflag", "1")
|
||||
return root
|
||||
|
||||
def handle_cardmng_authpass_request(self, request: Node) -> Node:
|
||||
# Given a dataid/refid previously found via inquire, verify the pin
|
||||
refid = request.attribute('refid')
|
||||
pin = request.attribute('pass')
|
||||
refid = request.attribute("refid")
|
||||
pin = request.attribute("pass")
|
||||
userid = self.data.local.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
valid = self.data.local.user.validate_pin(userid, pin)
|
||||
else:
|
||||
valid = False
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('status', str(Status.SUCCESS if valid else Status.INVALID_PIN))
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute(
|
||||
"status", str(Status.SUCCESS if valid else Status.INVALID_PIN)
|
||||
)
|
||||
return root
|
||||
|
||||
def handle_cardmng_getrefid_request(self, request: Node) -> Node:
|
||||
# Given a cardid and a pin, register the card with the system and generate a new dataid/refid + extid
|
||||
cardid = request.attribute('cardid')
|
||||
pin = request.attribute('passwd')
|
||||
cardid = request.attribute("cardid")
|
||||
pin = request.attribute("passwd")
|
||||
userid = self.data.local.user.create_account(cardid, pin)
|
||||
if userid is None:
|
||||
# This user can't be created
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
refid = self.data.local.user.create_refid(self.game, self.version, userid)
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('dataid', refid)
|
||||
root.set_attribute('refid', refid)
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("dataid", refid)
|
||||
root.set_attribute("refid", refid)
|
||||
return root
|
||||
|
||||
def handle_cardmng_bindmodel_request(self, request: Node) -> Node:
|
||||
# Given a refid, bind the user's card to the current version of the game
|
||||
refid = request.attribute('refid')
|
||||
refid = request.attribute("refid")
|
||||
userid = self.data.local.user.from_refid(self.game, self.version, refid)
|
||||
self.bind_profile(userid)
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('dataid', refid)
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("dataid", refid)
|
||||
return root
|
||||
|
||||
def handle_cardmng_getkeepspan_request(self, request: Node) -> Node:
|
||||
# Unclear what this method does, return an arbitrary span
|
||||
root = Node.void('cardmng')
|
||||
root.set_attribute('keepspan', '30')
|
||||
root = Node.void("cardmng")
|
||||
root.set_attribute("keepspan", "30")
|
||||
return root
|
||||
|
||||
def handle_cardmng_getdatalist_request(self, request: Node) -> Node:
|
||||
# Unclear what this method does, return a dummy response
|
||||
root = Node.void('cardmng')
|
||||
root = Node.void("cardmng")
|
||||
return root
|
||||
|
@ -16,42 +16,45 @@ class CoreHandler(Base):
|
||||
each server which handles a particular service. For us, this is always
|
||||
our URL since we serve everything.
|
||||
"""
|
||||
|
||||
def item(name: str, url: str) -> Node:
|
||||
node = Node.void('item')
|
||||
node.set_attribute('name', name)
|
||||
node.set_attribute('url', url)
|
||||
node = Node.void("item")
|
||||
node.set_attribute("name", name)
|
||||
node.set_attribute("url", url)
|
||||
return node
|
||||
|
||||
url = f'{"https" if self.config.server.https else "http"}://{self.config.server.address}:{self.config.server.port}/'
|
||||
root = Node.void('services')
|
||||
root.set_attribute('expire', '600')
|
||||
root = Node.void("services")
|
||||
root.set_attribute("expire", "600")
|
||||
# This can be set to 'operation', 'debug', 'test', and 'factory'.
|
||||
root.set_attribute('mode', 'operation')
|
||||
root.set_attribute('product_domain', '1')
|
||||
root.set_attribute("mode", "operation")
|
||||
root.set_attribute("product_domain", "1")
|
||||
|
||||
root.add_child(item('cardmng', url))
|
||||
root.add_child(item('dlstatus', url))
|
||||
root.add_child(item('eacoin', url))
|
||||
root.add_child(item('facility', url))
|
||||
root.add_child(item('lobby', url))
|
||||
root.add_child(item('local', url))
|
||||
root.add_child(item('message', url))
|
||||
root.add_child(item('package', url))
|
||||
root.add_child(item('pcbevent', url))
|
||||
root.add_child(item('pcbtracker', url))
|
||||
root.add_child(item('pkglist', url))
|
||||
root.add_child(item('posevent', url))
|
||||
root.add_child(item("cardmng", url))
|
||||
root.add_child(item("dlstatus", url))
|
||||
root.add_child(item("eacoin", url))
|
||||
root.add_child(item("facility", url))
|
||||
root.add_child(item("lobby", url))
|
||||
root.add_child(item("local", url))
|
||||
root.add_child(item("message", url))
|
||||
root.add_child(item("package", url))
|
||||
root.add_child(item("pcbevent", url))
|
||||
root.add_child(item("pcbtracker", url))
|
||||
root.add_child(item("pkglist", url))
|
||||
root.add_child(item("posevent", url))
|
||||
for srv in self.extra_services:
|
||||
root.add_child(item(srv, url))
|
||||
|
||||
root.add_child(item('ntp', 'ntp://pool.ntp.org/'))
|
||||
root.add_child(item("ntp", "ntp://pool.ntp.org/"))
|
||||
|
||||
# Translate keepalive to a raw IP because we can't give out a host here
|
||||
keepalive = socket.gethostbyname(self.config.server.keepalive)
|
||||
root.add_child(item(
|
||||
'keepalive',
|
||||
f'http://{keepalive}/core/keepalive?pa={keepalive}&ia={keepalive}&ga={keepalive}&ma={keepalive}&t1=2&t2=10',
|
||||
))
|
||||
root.add_child(
|
||||
item(
|
||||
"keepalive",
|
||||
f"http://{keepalive}/core/keepalive?pa={keepalive}&ia={keepalive}&ga={keepalive}&ma={keepalive}&t1=2&t2=10",
|
||||
)
|
||||
)
|
||||
return root
|
||||
|
||||
def handle_pcbtracker_alive_request(self, request: Node) -> Node:
|
||||
@ -60,9 +63,12 @@ class CoreHandler(Base):
|
||||
which returns whether PASELI should be active or not for this session.
|
||||
"""
|
||||
# Reports that a machine is booting. Overloaded to enable/disable paseli
|
||||
root = Node.void('pcbtracker')
|
||||
root.set_attribute('ecenable', '1' if (self.supports_paseli and self.config.paseli.enabled) else '0')
|
||||
root.set_attribute('expire', '600')
|
||||
root = Node.void("pcbtracker")
|
||||
root.set_attribute(
|
||||
"ecenable",
|
||||
"1" if (self.supports_paseli and self.config.paseli.enabled) else "0",
|
||||
)
|
||||
root.set_attribute("expire", "600")
|
||||
return root
|
||||
|
||||
def handle_pcbevent_put_request(self, request: Node) -> Node:
|
||||
@ -70,23 +76,23 @@ class CoreHandler(Base):
|
||||
Handle a PCBEvent request. We do nothing for this aside from logging the event.
|
||||
"""
|
||||
for item in request.children:
|
||||
if item.name == 'item':
|
||||
name = item.child_value('name')
|
||||
value = item.child_value('value')
|
||||
timestamp = item.child_value('time')
|
||||
if item.name == "item":
|
||||
name = item.child_value("name")
|
||||
value = item.child_value("value")
|
||||
timestamp = item.child_value("time")
|
||||
self.data.local.network.put_event(
|
||||
'pcbevent',
|
||||
"pcbevent",
|
||||
{
|
||||
'name': name,
|
||||
'value': value,
|
||||
'model': str(self.model),
|
||||
'pcbid': self.config.machine.pcbid,
|
||||
'ip': self.config.client.address,
|
||||
"name": name,
|
||||
"value": value,
|
||||
"model": str(self.model),
|
||||
"pcbid": self.config.machine.pcbid,
|
||||
"ip": self.config.client.address,
|
||||
},
|
||||
timestamp=timestamp,
|
||||
)
|
||||
|
||||
return Node.void('pcbevent')
|
||||
return Node.void("pcbevent")
|
||||
|
||||
def handle_package_list_request(self, request: Node) -> Node:
|
||||
"""
|
||||
@ -94,8 +100,8 @@ class CoreHandler(Base):
|
||||
We don't support this at the moment.
|
||||
"""
|
||||
# List all available update packages on the server
|
||||
root = Node.void('package')
|
||||
root.set_attribute('expire', '600')
|
||||
root = Node.void("package")
|
||||
root.set_attribute("expire", "600")
|
||||
return root
|
||||
|
||||
def handle_message_get_request(self, request: Node) -> Node:
|
||||
@ -103,8 +109,8 @@ class CoreHandler(Base):
|
||||
I have absolutely no fucking idea what this does, but it might be for
|
||||
operator messages?
|
||||
"""
|
||||
root = Node.void('message')
|
||||
root.set_attribute('expire', '600')
|
||||
root = Node.void("message")
|
||||
root.set_attribute("expire", "600")
|
||||
return root
|
||||
|
||||
def handle_dlstatus_progress_request(self, request: Node) -> Node:
|
||||
@ -112,7 +118,7 @@ class CoreHandler(Base):
|
||||
I have absolutely no fucking idea what this does either, download
|
||||
status reports maybe?
|
||||
"""
|
||||
return Node.void('dlstatus')
|
||||
return Node.void("dlstatus")
|
||||
|
||||
def handle_facility_get_request(self, request: Node) -> Node:
|
||||
"""
|
||||
@ -166,45 +172,57 @@ class CoreHandler(Base):
|
||||
country = "XX"
|
||||
regionstr = ""
|
||||
|
||||
root = Node.void('facility')
|
||||
root.set_attribute('expire', '600')
|
||||
location = Node.void('location')
|
||||
location.add_child(Node.string('id', ID.format_machine_id(machine.id, region=country)))
|
||||
location.add_child(Node.string('country', country))
|
||||
location.add_child(Node.string('region', regionstr))
|
||||
location.add_child(Node.string('name', machine.name))
|
||||
location.add_child(Node.u8('type', 0))
|
||||
root = Node.void("facility")
|
||||
root.set_attribute("expire", "600")
|
||||
location = Node.void("location")
|
||||
location.add_child(
|
||||
Node.string("id", ID.format_machine_id(machine.id, region=country))
|
||||
)
|
||||
location.add_child(Node.string("country", country))
|
||||
location.add_child(Node.string("region", regionstr))
|
||||
location.add_child(Node.string("name", machine.name))
|
||||
location.add_child(Node.u8("type", 0))
|
||||
|
||||
line = Node.void('line')
|
||||
line.add_child(Node.string('id', '.'))
|
||||
line.add_child(Node.u8('class', 0))
|
||||
line = Node.void("line")
|
||||
line.add_child(Node.string("id", "."))
|
||||
line.add_child(Node.u8("class", 0))
|
||||
|
||||
portfw = Node.void('portfw')
|
||||
portfw.add_child(Node.ipv4('globalip', self.config.client.address))
|
||||
portfw.add_child(Node.u16('globalport', machine.port))
|
||||
portfw.add_child(Node.u16('privateport', machine.port))
|
||||
portfw = Node.void("portfw")
|
||||
portfw.add_child(Node.ipv4("globalip", self.config.client.address))
|
||||
portfw.add_child(Node.u16("globalport", machine.port))
|
||||
portfw.add_child(Node.u16("privateport", machine.port))
|
||||
|
||||
public = Node.void('public')
|
||||
public.add_child(Node.u8('flag', 1))
|
||||
public.add_child(Node.string('name', '.'))
|
||||
public.add_child(Node.string('latitude', '0'))
|
||||
public.add_child(Node.string('longitude', '0'))
|
||||
public = Node.void("public")
|
||||
public.add_child(Node.u8("flag", 1))
|
||||
public.add_child(Node.string("name", "."))
|
||||
public.add_child(Node.string("latitude", "0"))
|
||||
public.add_child(Node.string("longitude", "0"))
|
||||
|
||||
share = Node.void('share')
|
||||
eacoin = Node.void('eacoin')
|
||||
eacoin.add_child(Node.s32('notchamount', 3000))
|
||||
eacoin.add_child(Node.s32('notchcount', 3))
|
||||
eacoin.add_child(Node.s32('supplylimit', 10000))
|
||||
share = Node.void("share")
|
||||
eacoin = Node.void("eacoin")
|
||||
eacoin.add_child(Node.s32("notchamount", 3000))
|
||||
eacoin.add_child(Node.s32("notchcount", 3))
|
||||
eacoin.add_child(Node.s32("supplylimit", 10000))
|
||||
|
||||
eapass = Node.void('eapass')
|
||||
eapass.add_child(Node.u16('valid', 365))
|
||||
eapass = Node.void("eapass")
|
||||
eapass.add_child(Node.u16("valid", 365))
|
||||
|
||||
url = Node.void('url')
|
||||
url.add_child(Node.string('eapass', self.config.server.uri or 'www.ea-pass.konami.net'))
|
||||
url.add_child(Node.string('arcadefan', self.config.server.uri or 'www.konami.jp/am'))
|
||||
url.add_child(Node.string('konaminetdx', self.config.server.uri or 'http://am.573.jp'))
|
||||
url.add_child(Node.string('konamiid', self.config.server.uri or 'https://id.konami.net'))
|
||||
url.add_child(Node.string('eagate', self.config.server.uri or 'http://eagate.573.jp'))
|
||||
url = Node.void("url")
|
||||
url.add_child(
|
||||
Node.string("eapass", self.config.server.uri or "www.ea-pass.konami.net")
|
||||
)
|
||||
url.add_child(
|
||||
Node.string("arcadefan", self.config.server.uri or "www.konami.jp/am")
|
||||
)
|
||||
url.add_child(
|
||||
Node.string("konaminetdx", self.config.server.uri or "http://am.573.jp")
|
||||
)
|
||||
url.add_child(
|
||||
Node.string("konamiid", self.config.server.uri or "https://id.konami.net")
|
||||
)
|
||||
url.add_child(
|
||||
Node.string("eagate", self.config.server.uri or "http://eagate.573.jp")
|
||||
)
|
||||
|
||||
share.add_child(eacoin)
|
||||
share.add_child(url)
|
||||
|
@ -27,32 +27,32 @@ class PASELIHandler(Base):
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
root = Node.void('eacoin')
|
||||
cardid = request.child_value('cardid')
|
||||
pin = request.child_value('passwd')
|
||||
root = Node.void("eacoin")
|
||||
cardid = request.child_value("cardid")
|
||||
pin = request.child_value("passwd")
|
||||
|
||||
if cardid is None or pin is None:
|
||||
# Refuse to return anything
|
||||
print("Invalid eacoin checkin request, missing cardid or pin")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
userid = self.data.local.user.from_cardid(cardid)
|
||||
if userid is None:
|
||||
# Refuse to do anything
|
||||
print("No user for eacoin checkin request")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
valid = self.data.local.user.validate_pin(userid, pin)
|
||||
if not valid:
|
||||
# Refuse to do anything
|
||||
print("User entered invalid pin for eacoin checkin request")
|
||||
root.set_attribute('status', str(Status.INVALID_PIN))
|
||||
root.set_attribute("status", str(Status.INVALID_PIN))
|
||||
return root
|
||||
|
||||
session = self.data.local.user.create_session(userid)
|
||||
@ -65,75 +65,77 @@ class PASELIHandler(Base):
|
||||
# enabled, so there's no way to find a balance.
|
||||
balance = 0
|
||||
else:
|
||||
balance = self.data.local.user.get_balance(userid, self.config.machine.arcade)
|
||||
balance = self.data.local.user.get_balance(
|
||||
userid, self.config.machine.arcade
|
||||
)
|
||||
|
||||
root.add_child(Node.s16('sequence', 0))
|
||||
root.add_child(Node.u8('acstatus', 0))
|
||||
root.add_child(Node.string('acid', 'DUMMY_ID'))
|
||||
root.add_child(Node.string('acname', 'DUMMY_NAME'))
|
||||
root.add_child(Node.s32('balance', balance))
|
||||
root.add_child(Node.string('sessid', session))
|
||||
root.add_child(Node.s16("sequence", 0))
|
||||
root.add_child(Node.u8("acstatus", 0))
|
||||
root.add_child(Node.string("acid", "DUMMY_ID"))
|
||||
root.add_child(Node.string("acname", "DUMMY_NAME"))
|
||||
root.add_child(Node.s32("balance", balance))
|
||||
root.add_child(Node.string("sessid", session))
|
||||
return root
|
||||
|
||||
def handle_eacoin_opcheckin_request(self, request: Node) -> Node:
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
root = Node.void('eacoin')
|
||||
passwd = request.child_value('passwd')
|
||||
root = Node.void("eacoin")
|
||||
passwd = request.child_value("passwd")
|
||||
|
||||
if passwd is None:
|
||||
# Refuse to return anything
|
||||
print("Invalid eacoin checkin request, missing passwd")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
if self.config.machine.arcade is None:
|
||||
# Machine doesn't belong to an arcade
|
||||
print("Machine doesn't belong to an arcade")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
||||
if arcade is None:
|
||||
# Refuse to do anything
|
||||
print("No arcade for operator checkin request")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
if arcade.pin != passwd:
|
||||
# Refuse to do anything
|
||||
print("User entered invalid pin for operator checkin request")
|
||||
root.set_attribute('status', str(Status.INVALID_PIN))
|
||||
root.set_attribute("status", str(Status.INVALID_PIN))
|
||||
return root
|
||||
|
||||
session = self.data.local.machine.create_session(arcade.id)
|
||||
root.add_child(Node.string('sessid', session))
|
||||
root.add_child(Node.string("sessid", session))
|
||||
return root
|
||||
|
||||
def handle_eacoin_consume_request(self, request: Node) -> Node:
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
def make_resp(status: int, balance: int) -> Node:
|
||||
root = Node.void('eacoin')
|
||||
root.add_child(Node.u8('acstatus', status))
|
||||
root.add_child(Node.u8('autocharge', 0))
|
||||
root.add_child(Node.s32('balance', balance))
|
||||
root = Node.void("eacoin")
|
||||
root.add_child(Node.u8("acstatus", status))
|
||||
root.add_child(Node.u8("autocharge", 0))
|
||||
root.add_child(Node.s32("balance", balance))
|
||||
return root
|
||||
|
||||
session = request.child_value('sessid')
|
||||
payment = request.child_value('payment')
|
||||
service = request.child_value('service')
|
||||
details = request.child_value('detail')
|
||||
session = request.child_value("sessid")
|
||||
payment = request.child_value("payment")
|
||||
service = request.child_value("service")
|
||||
details = request.child_value("detail")
|
||||
if session is None or payment is None:
|
||||
# Refuse to do anything
|
||||
print("Invalid eacoin consume request, missing sessid or payment")
|
||||
@ -156,20 +158,27 @@ class PASELIHandler(Base):
|
||||
else:
|
||||
# Look up the new balance based on this delta. If there isn't enough,
|
||||
# we will end up returning None here and exit without performing.
|
||||
balance = self.data.local.user.update_balance(userid, self.config.machine.arcade, -payment)
|
||||
balance = self.data.local.user.update_balance(
|
||||
userid, self.config.machine.arcade, -payment
|
||||
)
|
||||
|
||||
if balance is None:
|
||||
print("Not enough balance for eacoin consume request")
|
||||
return make_resp(1, self.data.local.user.get_balance(userid, self.config.machine.arcade))
|
||||
return make_resp(
|
||||
1,
|
||||
self.data.local.user.get_balance(
|
||||
userid, self.config.machine.arcade
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.data.local.network.put_event(
|
||||
'paseli_transaction',
|
||||
"paseli_transaction",
|
||||
{
|
||||
'delta': -payment,
|
||||
'balance': balance,
|
||||
'service': -service,
|
||||
'reason': details,
|
||||
'pcbid': self.config.machine.pcbid,
|
||||
"delta": -payment,
|
||||
"balance": balance,
|
||||
"service": -service,
|
||||
"reason": details,
|
||||
"pcbid": self.config.machine.pcbid,
|
||||
},
|
||||
userid=userid,
|
||||
arcadeid=self.config.machine.arcade,
|
||||
@ -181,16 +190,16 @@ class PASELIHandler(Base):
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
root = Node.void('eacoin')
|
||||
sessid = request.child_value('sessid')
|
||||
logtype = request.child_value('logtype')
|
||||
target = request.child_value('target')
|
||||
limit = request.child_value('perpage')
|
||||
offset = request.child_value('offset')
|
||||
root = Node.void("eacoin")
|
||||
sessid = request.child_value("sessid")
|
||||
logtype = request.child_value("logtype")
|
||||
target = request.child_value("target")
|
||||
limit = request.child_value("perpage")
|
||||
offset = request.child_value("offset")
|
||||
|
||||
# Try to determine whether its a user or an arcade session
|
||||
userid = self.data.local.user.from_session(sessid)
|
||||
@ -217,47 +226,59 @@ class PASELIHandler(Base):
|
||||
events = self.data.local.network.get_events(
|
||||
userid=userid,
|
||||
arcadeid=arcadeid,
|
||||
event='paseli_transaction',
|
||||
event="paseli_transaction",
|
||||
)
|
||||
|
||||
# Further filter it down to the current PCBID
|
||||
events = [event for event in events if event.data.get('pcbid') == target]
|
||||
events = [event for event in events if event.data.get("pcbid") == target]
|
||||
|
||||
# Grab the end of day today as a timestamp
|
||||
end_of_today = Time.end_of_today()
|
||||
time_format = '%Y-%m-%d %H:%M:%S'
|
||||
date_format = '%Y-%m-%d'
|
||||
time_format = "%Y-%m-%d %H:%M:%S"
|
||||
date_format = "%Y-%m-%d"
|
||||
|
||||
# Set up common structure
|
||||
lognode = Node.void(logtype)
|
||||
topic = Node.void('topic')
|
||||
topic = Node.void("topic")
|
||||
lognode.add_child(topic)
|
||||
summary = Node.void('summary')
|
||||
summary = Node.void("summary")
|
||||
lognode.add_child(summary)
|
||||
|
||||
# Display what day we are summed to
|
||||
topic.add_child(Node.string('sumdate', Time.format(Time.now(), date_format)))
|
||||
topic.add_child(Node.string("sumdate", Time.format(Time.now(), date_format)))
|
||||
|
||||
if logtype == 'last7days':
|
||||
if logtype == "last7days":
|
||||
# We show today in the today total, last 7 days prior in the week total
|
||||
beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
|
||||
end_of_week = beginning_of_today
|
||||
beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK
|
||||
|
||||
topic.add_child(Node.string('sumfrom', Time.format(beginning_of_week, date_format)))
|
||||
topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format)))
|
||||
today_total = sum([
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= beginning_of_today and event.timestamp < end_of_today
|
||||
])
|
||||
topic.add_child(
|
||||
Node.string("sumfrom", Time.format(beginning_of_week, date_format))
|
||||
)
|
||||
topic.add_child(Node.string("sumto", Time.format(end_of_week, date_format)))
|
||||
today_total = sum(
|
||||
[
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= beginning_of_today
|
||||
and event.timestamp < end_of_today
|
||||
]
|
||||
)
|
||||
|
||||
today_total = sum([
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= beginning_of_today and event.timestamp < end_of_today
|
||||
])
|
||||
today_total = sum(
|
||||
[
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= beginning_of_today
|
||||
and event.timestamp < end_of_today
|
||||
]
|
||||
)
|
||||
week_txns = [
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= beginning_of_week and event.timestamp < end_of_week
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= beginning_of_week
|
||||
and event.timestamp < end_of_week
|
||||
]
|
||||
week_total = sum(week_txns)
|
||||
if len(week_txns) > 0:
|
||||
@ -272,23 +293,38 @@ class PASELIHandler(Base):
|
||||
end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY)
|
||||
start_of_day = end_of_day - Time.SECONDS_IN_DAY
|
||||
|
||||
items.append(sum([
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= start_of_day and event.timestamp < end_of_day
|
||||
]))
|
||||
items.append(
|
||||
sum(
|
||||
[
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= start_of_day
|
||||
and event.timestamp < end_of_day
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
topic.add_child(Node.s32('today', today_total))
|
||||
topic.add_child(Node.s32('average', week_avg))
|
||||
topic.add_child(Node.s32('total', week_total))
|
||||
summary.add_child(Node.s32_array('items', items))
|
||||
topic.add_child(Node.s32("today", today_total))
|
||||
topic.add_child(Node.s32("average", week_avg))
|
||||
topic.add_child(Node.s32("total", week_total))
|
||||
summary.add_child(Node.s32_array("items", items))
|
||||
|
||||
if logtype == 'last52weeks':
|
||||
if logtype == "last52weeks":
|
||||
# Start one week back, since the operator can look at last7days for newer stuff.
|
||||
beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
|
||||
end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK
|
||||
|
||||
topic.add_child(Node.string('sumfrom', Time.format(end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format)))
|
||||
topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format)))
|
||||
topic.add_child(
|
||||
Node.string(
|
||||
"sumfrom",
|
||||
Time.format(
|
||||
end_of_52_weeks - (52 * Time.SECONDS_IN_WEEK), date_format
|
||||
),
|
||||
)
|
||||
)
|
||||
topic.add_child(
|
||||
Node.string("sumto", Time.format(end_of_52_weeks, date_format))
|
||||
)
|
||||
|
||||
# We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc...
|
||||
items = []
|
||||
@ -296,45 +332,53 @@ class PASELIHandler(Base):
|
||||
end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK)
|
||||
beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK
|
||||
|
||||
items.append(sum([
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= beginning_of_range and event.timestamp < end_of_range
|
||||
]))
|
||||
items.append(
|
||||
sum(
|
||||
[
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= beginning_of_range
|
||||
and event.timestamp < end_of_range
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
summary.add_child(Node.s32_array('items', items))
|
||||
summary.add_child(Node.s32_array("items", items))
|
||||
|
||||
if logtype == 'eachday':
|
||||
if logtype == "eachday":
|
||||
start_ts = Time.now()
|
||||
end_ts = Time.now()
|
||||
weekdays = [0] * 7
|
||||
|
||||
for event in events:
|
||||
event_day = Time.days_into_week(event.timestamp)
|
||||
weekdays[event_day] = weekdays[event_day] - event.data.get_int('delta')
|
||||
weekdays[event_day] = weekdays[event_day] - event.data.get_int("delta")
|
||||
if event.timestamp < start_ts:
|
||||
start_ts = event.timestamp
|
||||
|
||||
topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
|
||||
topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
|
||||
summary.add_child(Node.s32_array('items', weekdays))
|
||||
topic.add_child(Node.string("sumfrom", Time.format(start_ts, date_format)))
|
||||
topic.add_child(Node.string("sumto", Time.format(end_ts, date_format)))
|
||||
summary.add_child(Node.s32_array("items", weekdays))
|
||||
|
||||
if logtype == 'eachhour':
|
||||
if logtype == "eachhour":
|
||||
start_ts = Time.now()
|
||||
end_ts = Time.now()
|
||||
hours = [0] * 24
|
||||
|
||||
for event in events:
|
||||
event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR)
|
||||
hours[event_hour] = hours[event_hour] - event.data.get_int('delta')
|
||||
event_hour = int(
|
||||
(event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR
|
||||
)
|
||||
hours[event_hour] = hours[event_hour] - event.data.get_int("delta")
|
||||
if event.timestamp < start_ts:
|
||||
start_ts = event.timestamp
|
||||
|
||||
topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
|
||||
topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
|
||||
summary.add_child(Node.s32_array('items', hours))
|
||||
topic.add_child(Node.string("sumfrom", Time.format(start_ts, date_format)))
|
||||
topic.add_child(Node.string("sumto", Time.format(end_ts, date_format)))
|
||||
summary.add_child(Node.s32_array("items", hours))
|
||||
|
||||
if logtype == 'detail':
|
||||
history = Node.void('history')
|
||||
if logtype == "detail":
|
||||
history = Node.void("history")
|
||||
lognode.add_child(history)
|
||||
|
||||
# Respect details paging
|
||||
@ -345,7 +389,7 @@ class PASELIHandler(Base):
|
||||
|
||||
# Output the details themselves
|
||||
for event in events:
|
||||
card_no = ''
|
||||
card_no = ""
|
||||
if event.userid is not None:
|
||||
user = self.data.local.user.get_user(event.userid)
|
||||
if user is not None:
|
||||
@ -353,24 +397,30 @@ class PASELIHandler(Base):
|
||||
if len(cards) > 0:
|
||||
card_no = CardCipher.encode(cards[0])
|
||||
|
||||
item = Node.void('item')
|
||||
item = Node.void("item")
|
||||
history.add_child(item)
|
||||
item.add_child(Node.string('date', Time.format(event.timestamp, time_format)))
|
||||
item.add_child(Node.s32('consume', -event.data.get_int('delta')))
|
||||
item.add_child(Node.s32('service', -event.data.get_int('service')))
|
||||
item.add_child(Node.string('cardtype', ''))
|
||||
item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no))
|
||||
item.add_child(Node.string('title', ''))
|
||||
item.add_child(Node.string('systemid', ''))
|
||||
item.add_child(
|
||||
Node.string("date", Time.format(event.timestamp, time_format))
|
||||
)
|
||||
item.add_child(Node.s32("consume", -event.data.get_int("delta")))
|
||||
item.add_child(Node.s32("service", -event.data.get_int("service")))
|
||||
item.add_child(Node.string("cardtype", ""))
|
||||
item.add_child(
|
||||
Node.string("cardno", " " * self.paseli_padding + card_no)
|
||||
)
|
||||
item.add_child(Node.string("title", ""))
|
||||
item.add_child(Node.string("systemid", ""))
|
||||
|
||||
if logtype == 'lastmonths':
|
||||
if logtype == "lastmonths":
|
||||
year, month, _ = Time.todays_date()
|
||||
this_month = Time.timestamp_from_date(year, month)
|
||||
last_month = Time.timestamp_from_date(year, month - 1)
|
||||
month_before = Time.timestamp_from_date(year, month - 2)
|
||||
|
||||
topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format)))
|
||||
topic.add_child(Node.string('sumto', Time.format(this_month, date_format)))
|
||||
topic.add_child(
|
||||
Node.string("sumfrom", Time.format(month_before, date_format))
|
||||
)
|
||||
topic.add_child(Node.string("sumto", Time.format(this_month, date_format)))
|
||||
|
||||
for (start, end) in [(month_before, last_month), (last_month, this_month)]:
|
||||
year, month, _ = Time.date_from_timestamp(start)
|
||||
@ -384,18 +434,24 @@ class PASELIHandler(Base):
|
||||
items.append(0)
|
||||
else:
|
||||
# Sum up all the txns for this day
|
||||
items.append(sum([
|
||||
-event.data.get_int('delta') for event in events
|
||||
if event.timestamp >= begin_ts and event.timestamp < end_ts
|
||||
]))
|
||||
items.append(
|
||||
sum(
|
||||
[
|
||||
-event.data.get_int("delta")
|
||||
for event in events
|
||||
if event.timestamp >= begin_ts
|
||||
and event.timestamp < end_ts
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
item = Node.void('item')
|
||||
item = Node.void("item")
|
||||
summary.add_child(item)
|
||||
item.add_child(Node.s32('year', year))
|
||||
item.add_child(Node.s32('month', month))
|
||||
item.add_child(Node.s32_array('items', items))
|
||||
item.add_child(Node.s32("year", year))
|
||||
item.add_child(Node.s32("month", month))
|
||||
item.add_child(Node.s32_array("items", items))
|
||||
|
||||
root.add_child(Node.u8('processing', 0))
|
||||
root.add_child(Node.u8("processing", 0))
|
||||
root.add_child(lognode)
|
||||
return root
|
||||
|
||||
@ -403,37 +459,37 @@ class PASELIHandler(Base):
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
root = Node.void('eacoin')
|
||||
oldpass = request.child_value('passwd')
|
||||
newpass = request.child_value('newpasswd')
|
||||
root = Node.void("eacoin")
|
||||
oldpass = request.child_value("passwd")
|
||||
newpass = request.child_value("newpasswd")
|
||||
|
||||
if oldpass is None or newpass is None:
|
||||
# Refuse to return anything
|
||||
print("Invalid eacoin pass change request, missing passwd")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
if self.config.machine.arcade is None:
|
||||
# Machine doesn't belong to an arcade
|
||||
print("Machine doesn't belong to an arcade")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
||||
if arcade is None:
|
||||
# Refuse to do anything
|
||||
print("No arcade for operator pass change request")
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
if arcade.pin != oldpass:
|
||||
# Refuse to do anything
|
||||
print("User entered invalid pin for operator pass change request")
|
||||
root.set_attribute('status', str(Status.INVALID_PIN))
|
||||
root.set_attribute("status", str(Status.INVALID_PIN))
|
||||
return root
|
||||
|
||||
arcade.pin = newpass
|
||||
@ -444,30 +500,30 @@ class PASELIHandler(Base):
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
session = request.child_value('sessid')
|
||||
session = request.child_value("sessid")
|
||||
if session is not None:
|
||||
# Destroy the session so it can't be used for any other purchases
|
||||
self.data.local.user.destroy_session(session)
|
||||
|
||||
root = Node.void('eacoin')
|
||||
root = Node.void("eacoin")
|
||||
return root
|
||||
|
||||
def handle_eacoin_opcheckout_request(self, request: Node) -> Node:
|
||||
if not self.config.paseli.enabled:
|
||||
# Refuse to respond, we don't have PASELI enabled
|
||||
print("PASELI not enabled, ignoring eacoin request")
|
||||
root = Node.void('eacoin')
|
||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
||||
root = Node.void("eacoin")
|
||||
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||
return root
|
||||
|
||||
session = request.child_value('sessid')
|
||||
session = request.child_value("sessid")
|
||||
if session is not None:
|
||||
# Destroy the session so it can't be used for any other purchases
|
||||
self.data.local.machine.destroy_session(session)
|
||||
|
||||
root = Node.void('eacoin')
|
||||
root = Node.void("eacoin")
|
||||
return root
|
||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
||||
from bemani.common import Model, Profile, ValidatedDict, GameConstants, DBConstants, Time
|
||||
from bemani.common import (
|
||||
Model,
|
||||
Profile,
|
||||
ValidatedDict,
|
||||
GameConstants,
|
||||
DBConstants,
|
||||
Time,
|
||||
)
|
||||
from bemani.data import Config, Data, Score, UserID, ScoreSaveException
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -54,12 +61,12 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
# Return the local2 service so that DDR Ace will send certain packets.
|
||||
extra_services: List[str] = [
|
||||
'local2',
|
||||
"local2",
|
||||
]
|
||||
|
||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||
super().__init__(data, config, model)
|
||||
if model.rev == 'X':
|
||||
if model.rev == "X":
|
||||
self.omnimix = True
|
||||
else:
|
||||
self.omnimix = False
|
||||
@ -74,39 +81,39 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
"""
|
||||
Given a game's rank constant, return the rank as defined above.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def db_to_game_rank(self, db_rank: int) -> int:
|
||||
"""
|
||||
Given a rank as defined above, return the game's rank constant.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def game_to_db_chart(self, game_chart: int) -> int:
|
||||
"""
|
||||
Given a game's chart for a song, return the chart as defined above.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def db_to_game_chart(self, db_chart: int) -> int:
|
||||
"""
|
||||
Given a chart as defined above, return the game's chart constant.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def game_to_db_halo(self, game_halo: int) -> int:
|
||||
"""
|
||||
Given a game's halo constant, return the halo as defined above.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def db_to_game_halo(self, db_halo: int) -> int:
|
||||
"""
|
||||
Given a halo as defined above, return the game's halo constant.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def previous_version(self) -> Optional['DDRBase']:
|
||||
def previous_version(self) -> Optional["DDRBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -118,16 +125,20 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def format_scores(self, userid: UserID, profile: Profile, scores: List[Score]) -> Node:
|
||||
def format_scores(
|
||||
self, userid: UserID, profile: Profile, scores: List[Score]
|
||||
) -> Node:
|
||||
"""
|
||||
Base handler for a score list. Given a userid, profile and a score list,
|
||||
return a Node representing a score list. Should be overridden.
|
||||
"""
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -153,7 +164,9 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# Now, return it
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
def new_profile_by_refid(self, refid: Optional[str], name: Optional[str], area: Optional[int]) -> None:
|
||||
def new_profile_by_refid(
|
||||
self, refid: Optional[str], name: Optional[str], area: Optional[int]
|
||||
) -> None:
|
||||
"""
|
||||
Given a RefID and a name/area, create a new profile.
|
||||
"""
|
||||
@ -169,8 +182,8 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
'area': area,
|
||||
"name": name,
|
||||
"area": area,
|
||||
},
|
||||
)
|
||||
self.put_profile(userid, defaultprofile)
|
||||
@ -199,8 +212,8 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
rank: int,
|
||||
halo: int,
|
||||
combo: int,
|
||||
trace: Optional[List[int]]=None,
|
||||
ghost: Optional[str]=None,
|
||||
trace: Optional[List[int]] = None,
|
||||
ghost: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -219,7 +232,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
self.CHART_DOUBLE_EXPERT,
|
||||
self.CHART_DOUBLE_CHALLENGE,
|
||||
]:
|
||||
raise Exception(f'Invalid chart {chart}')
|
||||
raise Exception(f"Invalid chart {chart}")
|
||||
if halo not in [
|
||||
self.HALO_NONE,
|
||||
self.HALO_GOOD_FULL_COMBO,
|
||||
@ -227,7 +240,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
self.HALO_PERFECT_FULL_COMBO,
|
||||
self.HALO_MARVELOUS_FULL_COMBO,
|
||||
]:
|
||||
raise Exception(f'Invalid halo {halo}')
|
||||
raise Exception(f"Invalid halo {halo}")
|
||||
if rank not in [
|
||||
self.RANK_E,
|
||||
self.RANK_D,
|
||||
@ -246,7 +259,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
self.RANK_AA_PLUS,
|
||||
self.RANK_AAA,
|
||||
]:
|
||||
raise Exception(f'Invalid rank {rank}')
|
||||
raise Exception(f"Invalid rank {rank}")
|
||||
|
||||
if userid is not None:
|
||||
oldscore = self.data.local.music.get_score(
|
||||
@ -277,26 +290,26 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Save combo
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int("combo", combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
|
||||
# Save halo
|
||||
history.replace_int('halo', halo)
|
||||
scoredata.replace_int('halo', max(scoredata.get_int('halo'), halo))
|
||||
history.replace_int("halo", halo)
|
||||
scoredata.replace_int("halo", max(scoredata.get_int("halo"), halo))
|
||||
|
||||
# Save rank
|
||||
history.replace_int('rank', rank)
|
||||
scoredata.replace_int('rank', max(scoredata.get_int('rank'), rank))
|
||||
history.replace_int("rank", rank)
|
||||
scoredata.replace_int("rank", max(scoredata.get_int("rank"), rank))
|
||||
|
||||
# Save ghost steps
|
||||
if trace is not None:
|
||||
history.replace_int_array('trace', len(trace), trace)
|
||||
history.replace_int_array("trace", len(trace), trace)
|
||||
if raised:
|
||||
scoredata.replace_int_array('trace', len(trace), trace)
|
||||
scoredata.replace_int_array("trace", len(trace), trace)
|
||||
if ghost is not None:
|
||||
history.replace_str('ghost', ghost)
|
||||
history.replace_str("ghost", ghost)
|
||||
if raised:
|
||||
scoredata.replace_str('ghost', ghost)
|
||||
scoredata.replace_str("ghost", ghost)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
|
@ -7,60 +7,52 @@ from bemani.protocol import Node
|
||||
|
||||
|
||||
class DDRGameShopHandler(DDRBase):
|
||||
|
||||
def handle_game_shop_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.attribute('name'))
|
||||
self.update_machine_name(request.attribute("name"))
|
||||
|
||||
game = Node.void('game')
|
||||
game.set_attribute('stop', '0')
|
||||
game = Node.void("game")
|
||||
game.set_attribute("stop", "0")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameLogHandler(DDRBase):
|
||||
|
||||
def handle_game_log_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
|
||||
class DDRGameMessageHandler(DDRBase):
|
||||
|
||||
def handle_game_message_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
|
||||
class DDRGameRankingHandler(DDRBase):
|
||||
|
||||
def handle_game_ranking_request(self, request: Node) -> Node:
|
||||
# Ranking request, unknown what its for
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
|
||||
class DDRGameLockHandler(DDRBase):
|
||||
|
||||
def handle_game_lock_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game.set_attribute('now_login', '0')
|
||||
game = Node.void("game")
|
||||
game.set_attribute("now_login", "0")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameTaxInfoHandler(DDRBase):
|
||||
|
||||
def handle_game_tax_info_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
tax_info = Node.void('tax_info')
|
||||
game = Node.void("game")
|
||||
tax_info = Node.void("tax_info")
|
||||
game.add_child(tax_info)
|
||||
tax_info.set_attribute('tax_phase', '0')
|
||||
tax_info.set_attribute("tax_phase", "0")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameRecorderHandler(DDRBase):
|
||||
|
||||
def handle_game_recorder_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
|
||||
class DDRGameHiscoreHandler(DDRBase):
|
||||
|
||||
def handle_game_hiscore_request(self, request: Node) -> Node:
|
||||
records = self.data.remote.music.get_all_records(self.game, self.music_version)
|
||||
|
||||
@ -71,13 +63,16 @@ class DDRGameHiscoreHandler(DDRBase):
|
||||
sortedrecords[score.id] = {}
|
||||
sortedrecords[score.id][score.chart] = (userid, score)
|
||||
missing_profiles.append(userid)
|
||||
users = {userid: profile for (userid, profile) in self.get_any_profiles(missing_profiles)}
|
||||
users = {
|
||||
userid: profile
|
||||
for (userid, profile) in self.get_any_profiles(missing_profiles)
|
||||
}
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedrecords:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink_num', str(song))
|
||||
music.set_attribute("reclink_num", str(song))
|
||||
|
||||
for chart in sortedrecords[song]:
|
||||
userid, score = sortedrecords[song][chart]
|
||||
@ -86,36 +81,44 @@ class DDRGameHiscoreHandler(DDRBase):
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.string('name', users[userid].get_str('name')))
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('area', users[userid].get_int('area', self.get_machine_region())))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32('code', users[userid].extid))
|
||||
typenode.add_child(Node.string("name", users[userid].get_str("name")))
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(
|
||||
Node.u16(
|
||||
"area", users[userid].get_int("area", self.get_machine_region())
|
||||
)
|
||||
)
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
typenode.add_child(Node.u32("code", users[userid].extid))
|
||||
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameAreaHiscoreHandler(DDRBase):
|
||||
|
||||
def handle_game_area_hiscore_request(self, request: Node) -> Node:
|
||||
shop_area = int(request.attribute('shop_area'))
|
||||
shop_area = int(request.attribute("shop_area"))
|
||||
|
||||
# First, get all users that are in the current shop's area
|
||||
area_users = {
|
||||
uid: prof for (uid, prof) in self.data.local.user.get_all_profiles(self.game, self.version)
|
||||
if prof.get_int('area', self.get_machine_region()) == shop_area
|
||||
uid: prof
|
||||
for (uid, prof) in self.data.local.user.get_all_profiles(
|
||||
self.game, self.version
|
||||
)
|
||||
if prof.get_int("area", self.get_machine_region()) == shop_area
|
||||
}
|
||||
|
||||
# Second, look up records belonging only to those users
|
||||
records = self.data.local.music.get_all_records(self.game, self.music_version, userlist=list(area_users.keys()))
|
||||
records = self.data.local.music.get_all_records(
|
||||
self.game, self.music_version, userlist=list(area_users.keys())
|
||||
)
|
||||
|
||||
# Now, do the same lazy thing as 'hiscore' because I don't want
|
||||
# to think about how to change this knowing that we only pulled
|
||||
@ -126,15 +129,18 @@ class DDRGameAreaHiscoreHandler(DDRBase):
|
||||
area_records[score.id] = {}
|
||||
area_records[score.id][score.chart] = (userid, score)
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in area_records:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink_num', str(song))
|
||||
music.set_attribute("reclink_num", str(song))
|
||||
|
||||
for chart in area_records[song]:
|
||||
userid, score = area_records[song][chart]
|
||||
if area_users[userid].get_int('area', self.get_machine_region()) != shop_area:
|
||||
if (
|
||||
area_users[userid].get_int("area", self.get_machine_region())
|
||||
!= shop_area
|
||||
):
|
||||
# Don't return this, this user isn't in this area
|
||||
continue
|
||||
try:
|
||||
@ -142,29 +148,35 @@ class DDRGameAreaHiscoreHandler(DDRBase):
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.string('name', area_users[userid].get_str('name')))
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('area', area_users[userid].get_int('area', self.get_machine_region())))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32('code', area_users[userid].extid))
|
||||
typenode.add_child(
|
||||
Node.string("name", area_users[userid].get_str("name"))
|
||||
)
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(
|
||||
Node.u16(
|
||||
"area",
|
||||
area_users[userid].get_int("area", self.get_machine_region()),
|
||||
)
|
||||
)
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
typenode.add_child(Node.u32("code", area_users[userid].extid))
|
||||
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameScoreHandler(DDRBase):
|
||||
|
||||
def handle_game_score_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
songid = int(request.attribute('mid'))
|
||||
chart = self.game_to_db_chart(int(request.attribute('type')))
|
||||
refid = request.attribute("refid")
|
||||
songid = int(request.attribute("mid"))
|
||||
chart = self.game_to_db_chart(int(request.attribute("type")))
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
@ -185,22 +197,21 @@ class DDRGameScoreHandler(DDRBase):
|
||||
recentscores.append(0)
|
||||
|
||||
# Return the most recent five scores
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for i in range(len(recentscores)):
|
||||
game.set_attribute(f'sc{i + 1}', str(recentscores[i]))
|
||||
game.set_attribute(f"sc{i + 1}", str(recentscores[i]))
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameTraceHandler(DDRBase):
|
||||
|
||||
def handle_game_trace_request(self, request: Node) -> Node:
|
||||
extid = int(request.attribute('code'))
|
||||
chart = int(request.attribute('type'))
|
||||
cid = intish(request.attribute('cid'))
|
||||
mid = intish(request.attribute('mid'))
|
||||
extid = int(request.attribute("code"))
|
||||
chart = int(request.attribute("type"))
|
||||
cid = intish(request.attribute("cid"))
|
||||
mid = intish(request.attribute("mid"))
|
||||
|
||||
# Base packet is just game, if we find something we add to it
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
|
||||
# Rival trace loading
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
@ -217,9 +228,9 @@ class DDRGameTraceHandler(DDRBase):
|
||||
mid,
|
||||
self.game_to_db_chart(chart),
|
||||
)
|
||||
if songscore is not None and 'trace' in songscore.data:
|
||||
game.add_child(Node.u32('size', len(songscore.data['trace'])))
|
||||
game.add_child(Node.u8_array('trace', songscore.data['trace']))
|
||||
if songscore is not None and "trace" in songscore.data:
|
||||
game.add_child(Node.u32("size", len(songscore.data["trace"])))
|
||||
game.add_child(Node.u8_array("trace", songscore.data["trace"]))
|
||||
|
||||
elif cid is not None:
|
||||
# Load trace from achievement
|
||||
@ -228,35 +239,33 @@ class DDRGameTraceHandler(DDRBase):
|
||||
self.version,
|
||||
userid,
|
||||
(cid * 4) + chart,
|
||||
'course',
|
||||
"course",
|
||||
)
|
||||
if coursescore is not None and 'trace' in coursescore:
|
||||
game.add_child(Node.u32('size', len(coursescore['trace'])))
|
||||
game.add_child(Node.u8_array('trace', coursescore['trace']))
|
||||
if coursescore is not None and "trace" in coursescore:
|
||||
game.add_child(Node.u32("size", len(coursescore["trace"])))
|
||||
game.add_child(Node.u8_array("trace", coursescore["trace"]))
|
||||
|
||||
# Nothing found, return empty
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameLoadHandler(DDRBase):
|
||||
|
||||
def handle_game_load_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
refid = request.attribute("refid")
|
||||
profile = self.get_profile_by_refid(refid)
|
||||
if profile is not None:
|
||||
return profile
|
||||
|
||||
game = Node.void('game')
|
||||
game.set_attribute('none', '0')
|
||||
game = Node.void("game")
|
||||
game.set_attribute("none", "0")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameLoadDailyHandler(DDRBase):
|
||||
|
||||
def handle_game_load_daily_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
game = Node.void('game')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
game = Node.void("game")
|
||||
profiledict = None
|
||||
|
||||
if extid is not None:
|
||||
@ -272,24 +281,23 @@ class DDRGameLoadDailyHandler(DDRBase):
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Day play counts
|
||||
daycount = Node.void('daycount')
|
||||
daycount = Node.void("daycount")
|
||||
game.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
daycount.set_attribute("playcount", str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unclear how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
dailycombo = Node.void("dailycombo")
|
||||
game.add_child(dailycombo)
|
||||
dailycombo.set_attribute('daily_combo', str(0))
|
||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
||||
dailycombo.set_attribute("daily_combo", str(0))
|
||||
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameOldHandler(DDRBase):
|
||||
|
||||
def handle_game_old_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
game = Node.void('game')
|
||||
refid = request.attribute("refid")
|
||||
game = Node.void("game")
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
previous_version: Optional[DDRBase] = None
|
||||
@ -300,41 +308,40 @@ class DDRGameOldHandler(DDRBase):
|
||||
if previous_version is not None:
|
||||
oldprofile = previous_version.get_profile(userid)
|
||||
if oldprofile is not None:
|
||||
game.set_attribute('name', oldprofile.get_str('name'))
|
||||
game.set_attribute('area', str(oldprofile.get_int('area', self.get_machine_region())))
|
||||
game.set_attribute("name", oldprofile.get_str("name"))
|
||||
game.set_attribute(
|
||||
"area", str(oldprofile.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameNewHandler(DDRBase):
|
||||
|
||||
def handle_game_new_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
area = int(request.attribute('area'))
|
||||
name = request.attribute('name').strip()
|
||||
refid = request.attribute("refid")
|
||||
area = int(request.attribute("area"))
|
||||
name = request.attribute("name").strip()
|
||||
|
||||
# Create a new profile for this user!
|
||||
self.new_profile_by_refid(refid, name, area)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameSaveHandler(DDRBase):
|
||||
|
||||
def handle_game_save_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
refid = request.attribute("refid")
|
||||
self.put_profile_by_refid(refid, request)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameFriendHandler(DDRBase):
|
||||
|
||||
def handle_game_friend_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
extid = intish(request.attribute("code"))
|
||||
userid = None
|
||||
friend = None
|
||||
|
||||
@ -347,62 +354,63 @@ class DDRGameFriendHandler(DDRBase):
|
||||
|
||||
if friend is None:
|
||||
# Return an empty node to tell the game we don't have a player here
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
game = Node.void('game')
|
||||
game.set_attribute('data', '1')
|
||||
game.add_child(Node.u32('code', friend.extid))
|
||||
game.add_child(Node.string('name', friend.get_str('name')))
|
||||
game.add_child(Node.u8('area', friend.get_int('area', self.get_machine_region())))
|
||||
game.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
game.add_child(Node.u32('star', friend.get_int('star')))
|
||||
game = Node.void("game")
|
||||
game.set_attribute("data", "1")
|
||||
game.add_child(Node.u32("code", friend.extid))
|
||||
game.add_child(Node.string("name", friend.get_str("name")))
|
||||
game.add_child(
|
||||
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
game.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
game.add_child(Node.u32("star", friend.get_int("star")))
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in friend:
|
||||
title = Node.void('title')
|
||||
if "title" in friend:
|
||||
title = Node.void("title")
|
||||
game.add_child(title)
|
||||
titledict = friend.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = friend.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in friend:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in friend:
|
||||
title_gr = Node.void("title_gr")
|
||||
game.add_child(title_gr)
|
||||
title_grdict = friend.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = friend.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
game.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
game.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameLoadCourseHandler(DDRBase):
|
||||
|
||||
def handle_game_load_c_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if extid is not None:
|
||||
# Rival score loading
|
||||
@ -413,8 +421,10 @@ class DDRGameLoadCourseHandler(DDRBase):
|
||||
|
||||
coursedata = [0] * 3200
|
||||
if userid is not None:
|
||||
for course in self.data.local.user.get_achievements(self.game, self.version, userid):
|
||||
if course.type != 'course':
|
||||
for course in self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
):
|
||||
if course.type != "course":
|
||||
continue
|
||||
|
||||
# Grab course ID and chart (kinda pointless because we add it right back up
|
||||
@ -425,35 +435,36 @@ class DDRGameLoadCourseHandler(DDRBase):
|
||||
# Populate course data
|
||||
index = ((courseid * 4) + coursechart) * 8
|
||||
if index >= 0 and index <= (len(coursedata) - 8):
|
||||
coursedata[index + 0] = int(course.data.get_int('score') / 10000)
|
||||
coursedata[index + 1] = course.data.get_int('score') % 10000
|
||||
coursedata[index + 2] = course.data.get_int('combo')
|
||||
coursedata[index + 3] = self.db_to_game_rank(course.data.get_int('rank'))
|
||||
coursedata[index + 5] = course.data.get_int('stage')
|
||||
coursedata[index + 6] = course.data.get_int('combo_type')
|
||||
coursedata[index + 0] = int(course.data.get_int("score") / 10000)
|
||||
coursedata[index + 1] = course.data.get_int("score") % 10000
|
||||
coursedata[index + 2] = course.data.get_int("combo")
|
||||
coursedata[index + 3] = self.db_to_game_rank(
|
||||
course.data.get_int("rank")
|
||||
)
|
||||
coursedata[index + 5] = course.data.get_int("stage")
|
||||
coursedata[index + 6] = course.data.get_int("combo_type")
|
||||
|
||||
game = Node.void('game')
|
||||
game.add_child(Node.u16_array('course', coursedata))
|
||||
game = Node.void("game")
|
||||
game.add_child(Node.u16_array("course", coursedata))
|
||||
return game
|
||||
|
||||
|
||||
class DDRGameSaveCourseHandler(DDRBase):
|
||||
|
||||
def handle_game_save_c_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
courseid = int(request.attribute('cid'))
|
||||
chart = int(request.attribute('ctype'))
|
||||
refid = request.attribute("refid")
|
||||
courseid = int(request.attribute("cid"))
|
||||
chart = int(request.attribute("ctype"))
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
# Calculate statistics
|
||||
data = request.child('data')
|
||||
points = int(data.attribute('score'))
|
||||
combo = int(data.attribute('combo'))
|
||||
combo_type = int(data.attribute('combo_type'))
|
||||
stage = int(data.attribute('stage'))
|
||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
||||
trace = request.child_value('trace')
|
||||
data = request.child("data")
|
||||
points = int(data.attribute("score"))
|
||||
combo = int(data.attribute("combo"))
|
||||
combo_type = int(data.attribute("combo_type"))
|
||||
stage = int(data.attribute("stage"))
|
||||
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||
trace = request.child_value("trace")
|
||||
|
||||
# Grab the old course score
|
||||
oldcourse = self.data.local.user.get_achievement(
|
||||
@ -461,38 +472,38 @@ class DDRGameSaveCourseHandler(DDRBase):
|
||||
self.version,
|
||||
userid,
|
||||
(courseid * 4) + chart,
|
||||
'course',
|
||||
"course",
|
||||
)
|
||||
|
||||
if oldcourse is not None:
|
||||
highscore = points > oldcourse.get_int('score')
|
||||
highscore = points > oldcourse.get_int("score")
|
||||
|
||||
points = max(points, oldcourse.get_int('score'))
|
||||
combo = max(combo, oldcourse.get_int('combo'))
|
||||
stage = max(stage, oldcourse.get_int('stage'))
|
||||
rank = max(rank, oldcourse.get_int('rank'))
|
||||
combo_type = max(combo_type, oldcourse.get_int('combo_type'))
|
||||
points = max(points, oldcourse.get_int("score"))
|
||||
combo = max(combo, oldcourse.get_int("combo"))
|
||||
stage = max(stage, oldcourse.get_int("stage"))
|
||||
rank = max(rank, oldcourse.get_int("rank"))
|
||||
combo_type = max(combo_type, oldcourse.get_int("combo_type"))
|
||||
|
||||
if not highscore:
|
||||
# Don't overwrite the ghost for a non-highscore
|
||||
trace = oldcourse.get_int_array('trace', len(trace))
|
||||
trace = oldcourse.get_int_array("trace", len(trace))
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
(courseid * 4) + chart,
|
||||
'course',
|
||||
"course",
|
||||
{
|
||||
'score': points,
|
||||
'combo': combo,
|
||||
'stage': stage,
|
||||
'rank': rank,
|
||||
'combo_type': combo_type,
|
||||
'trace': trace,
|
||||
"score": points,
|
||||
"combo": combo,
|
||||
"stage": stage,
|
||||
"rank": rank,
|
||||
"combo_type": combo_type,
|
||||
"trace": trace,
|
||||
},
|
||||
)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
@ -53,7 +53,7 @@ class DDR2013(
|
||||
DDRBase,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution 2013'
|
||||
name: str = "DanceDanceRevolution 2013"
|
||||
version: int = VersionConstants.DDR_2013
|
||||
|
||||
GAME_STYLE_SINGLE: Final[int] = 0
|
||||
@ -160,30 +160,32 @@ class DDR2013(
|
||||
return combo_type
|
||||
|
||||
def handle_game_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for flagid in range(256):
|
||||
flag = Node.void('flag')
|
||||
flag = Node.void("flag")
|
||||
game.add_child(flag)
|
||||
|
||||
flag.set_attribute('id', str(flagid))
|
||||
flag.set_attribute('t', '0')
|
||||
flag.set_attribute('s1', '0')
|
||||
flag.set_attribute('s2', '0')
|
||||
flag.set_attribute('area', str(self.get_machine_region()))
|
||||
flag.set_attribute('is_final', '1')
|
||||
flag.set_attribute("id", str(flagid))
|
||||
flag.set_attribute("t", "0")
|
||||
flag.set_attribute("s1", "0")
|
||||
flag.set_attribute("s2", "0")
|
||||
flag.set_attribute("area", str(self.get_machine_region()))
|
||||
flag.set_attribute("is_final", "1")
|
||||
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music", counts_by_reflink))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if extid is not None:
|
||||
# Rival score loading
|
||||
@ -193,7 +195,9 @@ class DDR2013(
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
else:
|
||||
scores = []
|
||||
|
||||
@ -203,11 +207,11 @@ class DDR2013(
|
||||
sortedscores[score.id] = {}
|
||||
sortedscores[score.id][score.chart] = score
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedscores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink', str(song))
|
||||
music.set_attribute("reclink", str(song))
|
||||
|
||||
for chart in sortedscores[song]:
|
||||
score = sortedscores[song][chart]
|
||||
@ -216,41 +220,41 @@ class DDR2013(
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('count', score.plays))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(Node.u16("count", score.plays))
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
songid = int(request.attribute('mid'))
|
||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
||||
refid = request.attribute("refid")
|
||||
songid = int(request.attribute("mid"))
|
||||
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||
|
||||
# Calculate statistics
|
||||
data = request.child('data')
|
||||
points = int(data.attribute('score'))
|
||||
combo = int(data.attribute('combo'))
|
||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
||||
data = request.child("data")
|
||||
points = int(data.attribute("score"))
|
||||
combo = int(data.attribute("combo"))
|
||||
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||
if points == 1000000:
|
||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
||||
elif int(data.attribute('perf_fc')) != 0:
|
||||
elif int(data.attribute("perf_fc")) != 0:
|
||||
halo = self.HALO_PERFECT_FULL_COMBO
|
||||
elif int(data.attribute('great_fc')) != 0:
|
||||
elif int(data.attribute("great_fc")) != 0:
|
||||
halo = self.HALO_GREAT_FULL_COMBO
|
||||
elif int(data.attribute('good_fc')) != 0:
|
||||
elif int(data.attribute("good_fc")) != 0:
|
||||
halo = self.HALO_GOOD_FULL_COMBO
|
||||
else:
|
||||
halo = self.HALO_NONE
|
||||
trace = request.child_value('trace')
|
||||
trace = request.child_value("trace")
|
||||
|
||||
# Save the score, regardless of whether we have a refid. If we save
|
||||
# an anonymous score, it only goes into the DB to count against the
|
||||
@ -268,355 +272,371 @@ class DDR2013(
|
||||
)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('game')
|
||||
root = Node.void("game")
|
||||
|
||||
# Look up play stats we bridge to every mix
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Basic game settings
|
||||
root.add_child(Node.string('seq', ''))
|
||||
root.add_child(Node.u32('code', profile.extid))
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root.add_child(Node.u8('area', profile.get_int('area', self.get_machine_region())))
|
||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
||||
root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays')))
|
||||
root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
||||
root.add_child(Node.string("seq", ""))
|
||||
root.add_child(Node.u32("code", profile.extid))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
root.add_child(
|
||||
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
root.add_child(Node.u32("cnt_s", play_stats.get_int("single_plays")))
|
||||
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||
root.add_child(
|
||||
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||
) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
root.add_child(Node.u32("exp_o", profile.get_int("exp_o")))
|
||||
root.add_child(Node.u32("star", profile.get_int("star")))
|
||||
root.add_child(Node.u32("star_c", profile.get_int("star_c")))
|
||||
root.add_child(Node.u8("combo", profile.get_int("combo", 0)))
|
||||
root.add_child(Node.u8("timing_diff", profile.get_int("early_late", 0)))
|
||||
|
||||
# Character stuff
|
||||
chara = Node.void('chara')
|
||||
chara = Node.void("chara")
|
||||
root.add_child(chara)
|
||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
||||
root.add_child(Node.u16_array('chara_opt', profile.get_int_array('chara_opt', 96, [208] * 96)))
|
||||
chara.set_attribute("my", str(profile.get_int("chara", 30)))
|
||||
root.add_child(
|
||||
Node.u16_array(
|
||||
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||
)
|
||||
)
|
||||
|
||||
# Drill rankings
|
||||
if 'title_gr' in profile:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in profile:
|
||||
title_gr = Node.void("title_gr")
|
||||
root.add_child(title_gr)
|
||||
title_grdict = profile.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = profile.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Calorie mode
|
||||
if 'weight' in profile:
|
||||
if "weight" in profile:
|
||||
workouts = self.data.local.user.get_time_based_achievements(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
achievementtype='workout',
|
||||
achievementtype="workout",
|
||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
||||
)
|
||||
total = sum([w.data.get_int('calories') for w in workouts])
|
||||
workout = Node.void('workout')
|
||||
total = sum([w.data.get_int("calories") for w in workouts])
|
||||
workout = Node.void("workout")
|
||||
root.add_child(workout)
|
||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
||||
workout.set_attribute('day', str(total))
|
||||
workout.set_attribute('disp', '1')
|
||||
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||
workout.set_attribute("day", str(total))
|
||||
workout.set_attribute("disp", "1")
|
||||
|
||||
# Daily play counts
|
||||
daycount = Node.void('daycount')
|
||||
daycount = Node.void("daycount")
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
daycount.set_attribute("playcount", str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
dailycombo = Node.void("dailycombo")
|
||||
root.add_child(dailycombo)
|
||||
dailycombo.set_attribute('daily_combo', str(0))
|
||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
||||
dailycombo.set_attribute("daily_combo", str(0))
|
||||
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||
|
||||
# Last cursor settings
|
||||
last = Node.void('last')
|
||||
last = Node.void("last")
|
||||
root.add_child(last)
|
||||
lastdict = profile.get_dict('last')
|
||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -1)))
|
||||
last.set_attribute('fri', str(lastdict.get_int('rival1', -1))) # This literally goes to the same memory in 2013
|
||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
||||
lastdict = profile.get_dict("last")
|
||||
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||
last.set_attribute("rival3", str(lastdict.get_int("rival3", -1)))
|
||||
last.set_attribute(
|
||||
"fri", str(lastdict.get_int("rival1", -1))
|
||||
) # This literally goes to the same memory in 2013
|
||||
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||
last.set_attribute("cid", str(lastdict.get_int("cid")))
|
||||
last.set_attribute("ctype", str(lastdict.get_int("ctype")))
|
||||
last.set_attribute("sid", str(lastdict.get_int("sid")))
|
||||
|
||||
# Result stars
|
||||
result_star = Node.void('result_star')
|
||||
result_star = Node.void("result_star")
|
||||
root.add_child(result_star)
|
||||
result_stars = profile.get_int_array('result_stars', 9)
|
||||
result_stars = profile.get_int_array("result_stars", 9)
|
||||
for i in range(9):
|
||||
result_star.set_attribute(f'slot{i + 1}', str(result_stars[i]))
|
||||
result_star.set_attribute(f"slot{i + 1}", str(result_stars[i]))
|
||||
|
||||
# Target stuff
|
||||
target = Node.void('target')
|
||||
target = Node.void("target")
|
||||
root.add_child(target)
|
||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
||||
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
root.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
root.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Options in menus
|
||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
|
||||
root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))
|
||||
root.add_child(Node.s16_array("opt", profile.get_int_array("opt", 16)))
|
||||
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||
|
||||
# Unlock flags
|
||||
root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 256, [1] * 256)))
|
||||
root.add_child(
|
||||
Node.u8_array("flag", profile.get_int_array("flag", 256, [1] * 256))
|
||||
)
|
||||
|
||||
# Ranking display?
|
||||
root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))
|
||||
root.add_child(Node.u16_array("rank", profile.get_int_array("rank", 100)))
|
||||
|
||||
# Rivals
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
friend = self.get_profile(link.other_userid)
|
||||
play_stats = self.get_play_statistics(link.other_userid)
|
||||
if friend is not None:
|
||||
friendnode = Node.void('friend')
|
||||
friendnode = Node.void("friend")
|
||||
root.add_child(friendnode)
|
||||
friendnode.set_attribute('pos', str(pos))
|
||||
friendnode.set_attribute('vs', '0')
|
||||
friendnode.set_attribute('up', '0')
|
||||
friendnode.add_child(Node.u32('code', friend.extid))
|
||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
||||
friendnode.add_child(Node.u8('area', friend.get_int('area', self.get_machine_region())))
|
||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
||||
friendnode.set_attribute("pos", str(pos))
|
||||
friendnode.set_attribute("vs", "0")
|
||||
friendnode.set_attribute("up", "0")
|
||||
friendnode.add_child(Node.u32("code", friend.extid))
|
||||
friendnode.add_child(Node.string("name", friend.get_str("name")))
|
||||
friendnode.add_child(
|
||||
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in friend:
|
||||
title = Node.void('title')
|
||||
if "title" in friend:
|
||||
title = Node.void("title")
|
||||
friendnode.add_child(title)
|
||||
titledict = friend.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = friend.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in friend:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in friend:
|
||||
title_gr = Node.void("title_gr")
|
||||
friendnode.add_child(title_gr)
|
||||
title_grdict = friend.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = friend.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
friendnode.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
friendnode.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Play area
|
||||
areas = profile.get_int_array('play_area', 55)
|
||||
play_area = Node.void('play_area')
|
||||
areas = profile.get_int_array("play_area", 55)
|
||||
play_area = Node.void("play_area")
|
||||
root.add_child(play_area)
|
||||
for i in range(len(areas)):
|
||||
play_area.set_attribute(f'play_cnt{i}', str(areas[i]))
|
||||
play_area.set_attribute(f"play_cnt{i}", str(areas[i]))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Grab last node and accessories so we can make decisions based on type
|
||||
last = request.child('last')
|
||||
lastdict = newprofile.get_dict('last')
|
||||
mode = int(last.attribute('mode'))
|
||||
style = int(last.attribute('style'))
|
||||
last = request.child("last")
|
||||
lastdict = newprofile.get_dict("last")
|
||||
mode = int(last.attribute("mode"))
|
||||
style = int(last.attribute("style"))
|
||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||
|
||||
# Drill rankings
|
||||
title = request.child('title')
|
||||
title_gr = request.child('title_gr')
|
||||
titledict = newprofile.get_dict('title')
|
||||
title_grdict = newprofile.get_dict('title_gr')
|
||||
title = request.child("title")
|
||||
title_gr = request.child("title_gr")
|
||||
titledict = newprofile.get_dict("title")
|
||||
title_grdict = newprofile.get_dict("title_gr")
|
||||
|
||||
# Groove radar level ups
|
||||
gr = request.child('gr')
|
||||
gr = request.child("gr")
|
||||
|
||||
# Set the correct values depending on if we're single or double play
|
||||
if is_dp:
|
||||
play_stats.increment_int('double_plays')
|
||||
play_stats.increment_int("double_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_d',
|
||||
"gr_d",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
title_grdict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
else:
|
||||
play_stats.increment_int('single_plays')
|
||||
play_stats.increment_int("single_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_s',
|
||||
"gr_s",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
play_stats.increment_int(f'cnt_m{mode}')
|
||||
title_grdict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
play_stats.increment_int(f"cnt_m{mode}")
|
||||
|
||||
# Result stars
|
||||
result_star = request.child('result_star')
|
||||
result_star = request.child("result_star")
|
||||
if result_star is not None:
|
||||
newprofile.replace_int_array(
|
||||
'result_stars',
|
||||
"result_stars",
|
||||
9,
|
||||
[
|
||||
intish(result_star.attribute('slot1')),
|
||||
intish(result_star.attribute('slot2')),
|
||||
intish(result_star.attribute('slot3')),
|
||||
intish(result_star.attribute('slot4')),
|
||||
intish(result_star.attribute('slot5')),
|
||||
intish(result_star.attribute('slot6')),
|
||||
intish(result_star.attribute('slot7')),
|
||||
intish(result_star.attribute('slot8')),
|
||||
intish(result_star.attribute('slot9')),
|
||||
intish(result_star.attribute("slot1")),
|
||||
intish(result_star.attribute("slot2")),
|
||||
intish(result_star.attribute("slot3")),
|
||||
intish(result_star.attribute("slot4")),
|
||||
intish(result_star.attribute("slot5")),
|
||||
intish(result_star.attribute("slot6")),
|
||||
intish(result_star.attribute("slot7")),
|
||||
intish(result_star.attribute("slot8")),
|
||||
intish(result_star.attribute("slot9")),
|
||||
],
|
||||
)
|
||||
|
||||
# Target stuff
|
||||
target = request.child('target')
|
||||
target = request.child("target")
|
||||
if target is not None:
|
||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
||||
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||
|
||||
# Update last attributes
|
||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Grab character options
|
||||
chara = request.child('chara')
|
||||
chara = request.child("chara")
|
||||
if chara is not None:
|
||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
||||
chara_opt = request.child('chara_opt')
|
||||
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||
chara_opt = request.child("chara_opt")
|
||||
if chara_opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('chara_opt', 96, chara_opt.value[:96])
|
||||
newprofile.replace_int_array("chara_opt", 96, chara_opt.value[:96])
|
||||
|
||||
# Options
|
||||
opt = request.child('opt')
|
||||
opt = request.child("opt")
|
||||
if opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('opt', 16, opt.value[:16])
|
||||
newprofile.replace_int_array("opt", 16, opt.value[:16])
|
||||
|
||||
# Experience and stars
|
||||
exp = request.child_value('exp')
|
||||
exp = request.child_value("exp")
|
||||
if exp is not None:
|
||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
||||
star = request.child_value('star')
|
||||
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||
star = request.child_value("star")
|
||||
if star is not None:
|
||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
||||
star_c = request.child_value('star_c')
|
||||
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||
star_c = request.child_value("star_c")
|
||||
if star_c is not None:
|
||||
newprofile.replace_int('star_c', newprofile.get_int('star_c') + exp)
|
||||
newprofile.replace_int("star_c", newprofile.get_int("star_c") + exp)
|
||||
|
||||
# Update game flags
|
||||
for child in request.children:
|
||||
if child.name != 'flag':
|
||||
if child.name != "flag":
|
||||
continue
|
||||
try:
|
||||
value = int(child.attribute('data'))
|
||||
offset = int(child.attribute('no'))
|
||||
value = int(child.attribute("data"))
|
||||
offset = int(child.attribute("no"))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
flags = newprofile.get_int_array('flag', 256, [1] * 256)
|
||||
flags = newprofile.get_int_array("flag", 256, [1] * 256)
|
||||
if offset < 0 or offset >= len(flags):
|
||||
continue
|
||||
flags[offset] = value
|
||||
newprofile.replace_int_array('flag', 256, flags)
|
||||
newprofile.replace_int_array("flag", 256, flags)
|
||||
|
||||
# Workout mode support
|
||||
newweight = -1
|
||||
oldweight = newprofile.get_int('weight')
|
||||
oldweight = newprofile.get_int("weight")
|
||||
for child in request.children:
|
||||
if child.name != 'weight':
|
||||
if child.name != "weight":
|
||||
continue
|
||||
newweight = child.value
|
||||
if newweight < 0:
|
||||
@ -625,14 +645,14 @@ class DDR2013(
|
||||
# Either update or unset the weight depending on the game
|
||||
if newweight == 0:
|
||||
# Weight is unset or we declined to use this feature, remove from profile
|
||||
if 'weight' in newprofile:
|
||||
del newprofile['weight']
|
||||
if "weight" in newprofile:
|
||||
del newprofile["weight"]
|
||||
else:
|
||||
# Weight has been set or previously retrieved, we should save calories
|
||||
newprofile.replace_int('weight', newweight)
|
||||
newprofile.replace_int("weight", newweight)
|
||||
total = 0
|
||||
for child in request.children:
|
||||
if child.name != 'calory':
|
||||
if child.name != "calory":
|
||||
continue
|
||||
total += child.value
|
||||
self.data.local.user.put_time_based_achievement(
|
||||
@ -640,10 +660,10 @@ class DDR2013(
|
||||
self.version,
|
||||
userid,
|
||||
0,
|
||||
'workout',
|
||||
"workout",
|
||||
{
|
||||
'calories': total,
|
||||
'weight': newweight,
|
||||
"calories": total,
|
||||
"weight": newweight,
|
||||
},
|
||||
)
|
||||
|
||||
@ -651,7 +671,7 @@ class DDR2013(
|
||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
@ -660,11 +680,11 @@ class DDR2013(
|
||||
# Save any rivals that were added/removed/changed
|
||||
newfriends = oldfriends[:]
|
||||
for child in request.children:
|
||||
if child.name != 'friend':
|
||||
if child.name != "friend":
|
||||
continue
|
||||
|
||||
code = int(child.attribute('code'))
|
||||
pos = int(child.attribute('pos'))
|
||||
code = int(child.attribute("code"))
|
||||
pos = int(child.attribute("pos"))
|
||||
|
||||
if pos >= 0 and pos < 10:
|
||||
if code == 0:
|
||||
@ -672,7 +692,9 @@ class DDR2013(
|
||||
newfriends[pos] = None
|
||||
else:
|
||||
# Try looking up the userid
|
||||
newfriends[pos] = self.data.remote.user.from_extid(self.game, self.version, code)
|
||||
newfriends[pos] = self.data.remote.user.from_extid(
|
||||
self.game, self.version, code
|
||||
)
|
||||
|
||||
# Diff the set of links to determine updates
|
||||
for i in range(10):
|
||||
@ -685,7 +707,7 @@ class DDR2013(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
elif oldfriends[i] is None:
|
||||
@ -694,7 +716,7 @@ class DDR2013(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
@ -704,24 +726,24 @@ class DDR2013(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
self.data.local.user.put_link(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
|
||||
# Play area counter
|
||||
shop_area = int(request.attribute('shop_area'))
|
||||
shop_area = int(request.attribute("shop_area"))
|
||||
if shop_area >= 0 and shop_area < 55:
|
||||
areas = newprofile.get_int_array('play_area', 55)
|
||||
areas = newprofile.get_int_array("play_area", 55)
|
||||
areas[shop_area] = areas[shop_area] + 1
|
||||
newprofile.replace_int_array('play_area', 55, areas)
|
||||
newprofile.replace_int_array("play_area", 55, areas)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid, play_stats)
|
||||
|
@ -47,7 +47,7 @@ class DDR2014(
|
||||
DDRBase,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution 2014'
|
||||
name: str = "DanceDanceRevolution 2014"
|
||||
version: int = VersionConstants.DDR_2014
|
||||
|
||||
GAME_STYLE_SINGLE: Final[int] = 0
|
||||
@ -154,47 +154,53 @@ class DDR2014(
|
||||
return combo_type
|
||||
|
||||
def handle_game_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for flagid in range(512):
|
||||
flag = Node.void('flag')
|
||||
flag = Node.void("flag")
|
||||
game.add_child(flag)
|
||||
|
||||
flag.set_attribute('id', str(flagid))
|
||||
flag.set_attribute('t', '0')
|
||||
flag.set_attribute('s1', '0')
|
||||
flag.set_attribute('s2', '0')
|
||||
flag.set_attribute('area', str(self.get_machine_region()))
|
||||
flag.set_attribute('is_final', '0')
|
||||
flag.set_attribute("id", str(flagid))
|
||||
flag.set_attribute("t", "0")
|
||||
flag.set_attribute("s1", "0")
|
||||
flag.set_attribute("s2", "0")
|
||||
flag.set_attribute("area", str(self.get_machine_region()))
|
||||
flag.set_attribute("is_final", "0")
|
||||
|
||||
# Last month's hit chart
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS, 30)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS, 30
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music_monthly', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music_monthly", counts_by_reflink))
|
||||
|
||||
# Last week's hit chart
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS, 7)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS, 7
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music_weekly', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music_weekly", counts_by_reflink))
|
||||
|
||||
# Last day's hit chart
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS, 1)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS, 1
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music_daily', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music_daily", counts_by_reflink))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if extid is not None:
|
||||
# Rival score loading
|
||||
@ -204,7 +210,9 @@ class DDR2014(
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
else:
|
||||
scores = []
|
||||
|
||||
@ -214,11 +222,11 @@ class DDR2014(
|
||||
sortedscores[score.id] = {}
|
||||
sortedscores[score.id][score.chart] = score
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedscores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink', str(song))
|
||||
music.set_attribute("reclink", str(song))
|
||||
|
||||
for chart in sortedscores[song]:
|
||||
score = sortedscores[song][chart]
|
||||
@ -227,17 +235,17 @@ class DDR2014(
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('count', score.plays))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(Node.u16("count", score.plays))
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
# The game optionally receives hard, life8, life4, risky, assist_clear, normal_clear
|
||||
# u8 values too, and saves music scores with these set, but the UI doesn't appear to
|
||||
# do anything with them, so we don't care.
|
||||
@ -245,26 +253,26 @@ class DDR2014(
|
||||
return game
|
||||
|
||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
songid = int(request.attribute('mid'))
|
||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
||||
refid = request.attribute("refid")
|
||||
songid = int(request.attribute("mid"))
|
||||
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||
|
||||
# Calculate statistics
|
||||
data = request.child('data')
|
||||
points = int(data.attribute('score'))
|
||||
combo = int(data.attribute('combo'))
|
||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
||||
data = request.child("data")
|
||||
points = int(data.attribute("score"))
|
||||
combo = int(data.attribute("combo"))
|
||||
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||
if points == 1000000:
|
||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
||||
elif int(data.attribute('perf_fc')) != 0:
|
||||
elif int(data.attribute("perf_fc")) != 0:
|
||||
halo = self.HALO_PERFECT_FULL_COMBO
|
||||
elif int(data.attribute('great_fc')) != 0:
|
||||
elif int(data.attribute("great_fc")) != 0:
|
||||
halo = self.HALO_GREAT_FULL_COMBO
|
||||
elif int(data.attribute('good_fc')) != 0:
|
||||
elif int(data.attribute("good_fc")) != 0:
|
||||
halo = self.HALO_GOOD_FULL_COMBO
|
||||
else:
|
||||
halo = self.HALO_NONE
|
||||
trace = request.child_value('trace')
|
||||
trace = request.child_value("trace")
|
||||
|
||||
# Save the score, regardless of whether we have a refid. If we save
|
||||
# an anonymous score, it only goes into the DB to count against the
|
||||
@ -282,24 +290,24 @@ class DDR2014(
|
||||
)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
def handle_game_load_edit_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_save_resultshot_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_trace_request(self, request: Node) -> Node:
|
||||
# This is almost identical to 2013 and below, except it will never
|
||||
# even try to request course traces, so we fork from common functionality.
|
||||
extid = int(request.attribute('code'))
|
||||
chart = int(request.attribute('type'))
|
||||
mid = intish(request.attribute('mid'))
|
||||
extid = int(request.attribute("code"))
|
||||
chart = int(request.attribute("type"))
|
||||
mid = intish(request.attribute("mid"))
|
||||
|
||||
# Base packet is just game, if we find something we add to it
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
|
||||
# Rival trace loading
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
@ -315,369 +323,389 @@ class DDR2014(
|
||||
mid,
|
||||
self.game_to_db_chart(chart),
|
||||
)
|
||||
if songscore is not None and 'trace' in songscore.data:
|
||||
game.add_child(Node.u32('size', len(songscore.data['trace'])))
|
||||
game.add_child(Node.u8_array('trace', songscore.data['trace']))
|
||||
if songscore is not None and "trace" in songscore.data:
|
||||
game.add_child(Node.u32("size", len(songscore.data["trace"])))
|
||||
game.add_child(Node.u8_array("trace", songscore.data["trace"]))
|
||||
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('game')
|
||||
root = Node.void("game")
|
||||
|
||||
# Look up play stats we bridge to every mix
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Basic game settings
|
||||
root.add_child(Node.string('seq', ''))
|
||||
root.add_child(Node.u32('code', profile.extid))
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root.add_child(Node.u8('area', profile.get_int('area', self.get_machine_region())))
|
||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
||||
root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays')))
|
||||
root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
||||
root.add_child(Node.string("seq", ""))
|
||||
root.add_child(Node.u32("code", profile.extid))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
root.add_child(
|
||||
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
root.add_child(Node.u32("cnt_s", play_stats.get_int("single_plays")))
|
||||
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||
root.add_child(
|
||||
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||
) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
root.add_child(Node.u32("exp_o", profile.get_int("exp_o")))
|
||||
root.add_child(Node.u32("star", profile.get_int("star")))
|
||||
root.add_child(Node.u32("star_c", profile.get_int("star_c")))
|
||||
root.add_child(Node.u8("combo", profile.get_int("combo", 0)))
|
||||
root.add_child(Node.u8("timing_diff", profile.get_int("early_late", 0)))
|
||||
|
||||
# Character stuff
|
||||
chara = Node.void('chara')
|
||||
chara = Node.void("chara")
|
||||
root.add_child(chara)
|
||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
||||
root.add_child(Node.u16_array('chara_opt', profile.get_int_array('chara_opt', 96, [208] * 96)))
|
||||
chara.set_attribute("my", str(profile.get_int("chara", 30)))
|
||||
root.add_child(
|
||||
Node.u16_array(
|
||||
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||
)
|
||||
)
|
||||
|
||||
# Drill rankings
|
||||
if 'title_gr' in profile:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in profile:
|
||||
title_gr = Node.void("title_gr")
|
||||
root.add_child(title_gr)
|
||||
title_grdict = profile.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = profile.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Calorie mode
|
||||
if 'weight' in profile:
|
||||
if "weight" in profile:
|
||||
workouts = self.data.local.user.get_time_based_achievements(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
achievementtype='workout',
|
||||
achievementtype="workout",
|
||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
||||
)
|
||||
total = sum([w.data.get_int('calories') for w in workouts])
|
||||
workout = Node.void('workout')
|
||||
total = sum([w.data.get_int("calories") for w in workouts])
|
||||
workout = Node.void("workout")
|
||||
root.add_child(workout)
|
||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
||||
workout.set_attribute('day', str(total))
|
||||
workout.set_attribute('disp', '1')
|
||||
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||
workout.set_attribute("day", str(total))
|
||||
workout.set_attribute("disp", "1")
|
||||
|
||||
# Unsure if this should be last day, or total calories ever
|
||||
totalcalorie = Node.void('totalcalorie')
|
||||
totalcalorie = Node.void("totalcalorie")
|
||||
root.add_child(totalcalorie)
|
||||
totalcalorie.set_attribute('total', str(total))
|
||||
totalcalorie.set_attribute("total", str(total))
|
||||
|
||||
# Daily play counts
|
||||
daycount = Node.void('daycount')
|
||||
daycount = Node.void("daycount")
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
daycount.set_attribute("playcount", str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
dailycombo = Node.void("dailycombo")
|
||||
root.add_child(dailycombo)
|
||||
dailycombo.set_attribute('daily_combo', str(0))
|
||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
||||
dailycombo.set_attribute("daily_combo", str(0))
|
||||
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||
|
||||
# Last cursor settings
|
||||
last = Node.void('last')
|
||||
last = Node.void("last")
|
||||
root.add_child(last)
|
||||
lastdict = profile.get_dict('last')
|
||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -1)))
|
||||
last.set_attribute('fri', str(lastdict.get_int('rival1', -1))) # This literally goes to the same memory in 2014
|
||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
||||
lastdict = profile.get_dict("last")
|
||||
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||
last.set_attribute("rival3", str(lastdict.get_int("rival3", -1)))
|
||||
last.set_attribute(
|
||||
"fri", str(lastdict.get_int("rival1", -1))
|
||||
) # This literally goes to the same memory in 2014
|
||||
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||
last.set_attribute("cid", str(lastdict.get_int("cid")))
|
||||
last.set_attribute("ctype", str(lastdict.get_int("ctype")))
|
||||
last.set_attribute("sid", str(lastdict.get_int("sid")))
|
||||
|
||||
# Result stars
|
||||
result_star = Node.void('result_star')
|
||||
result_star = Node.void("result_star")
|
||||
root.add_child(result_star)
|
||||
result_stars = profile.get_int_array('result_stars', 9)
|
||||
result_stars = profile.get_int_array("result_stars", 9)
|
||||
for i in range(9):
|
||||
result_star.set_attribute(f'slot{i + 1}', str(result_stars[i]))
|
||||
result_star.set_attribute(f"slot{i + 1}", str(result_stars[i]))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
root.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
root.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Options in menus
|
||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
|
||||
root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))
|
||||
option_ver = Node.void('option_ver')
|
||||
root.add_child(Node.s16_array("opt", profile.get_int_array("opt", 16)))
|
||||
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||
option_ver = Node.void("option_ver")
|
||||
root.add_child(option_ver)
|
||||
option_ver.set_attribute('ver', str(profile.get_int('option_ver', 2)))
|
||||
if 'option_02' in profile:
|
||||
root.add_child(Node.s16_array('option_02', profile.get_int_array('option_02', 24)))
|
||||
option_ver.set_attribute("ver", str(profile.get_int("option_ver", 2)))
|
||||
if "option_02" in profile:
|
||||
root.add_child(
|
||||
Node.s16_array("option_02", profile.get_int_array("option_02", 24))
|
||||
)
|
||||
|
||||
# Unlock flags
|
||||
root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 512, [1] * 512)[:256]))
|
||||
root.add_child(Node.u8_array('flag_ex', profile.get_int_array('flag', 512, [1] * 512)))
|
||||
root.add_child(
|
||||
Node.u8_array("flag", profile.get_int_array("flag", 512, [1] * 512)[:256])
|
||||
)
|
||||
root.add_child(
|
||||
Node.u8_array("flag_ex", profile.get_int_array("flag", 512, [1] * 512))
|
||||
)
|
||||
|
||||
# Ranking display?
|
||||
root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))
|
||||
root.add_child(Node.u16_array("rank", profile.get_int_array("rank", 100)))
|
||||
|
||||
# Rivals
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
friend = self.get_profile(link.other_userid)
|
||||
play_stats = self.get_play_statistics(link.other_userid)
|
||||
if friend is not None:
|
||||
friendnode = Node.void('friend')
|
||||
friendnode = Node.void("friend")
|
||||
root.add_child(friendnode)
|
||||
friendnode.set_attribute('pos', str(pos))
|
||||
friendnode.set_attribute('vs', '0')
|
||||
friendnode.set_attribute('up', '0')
|
||||
friendnode.add_child(Node.u32('code', friend.extid))
|
||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
||||
friendnode.add_child(Node.u8('area', friend.get_int('area', self.get_machine_region())))
|
||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
||||
friendnode.set_attribute("pos", str(pos))
|
||||
friendnode.set_attribute("vs", "0")
|
||||
friendnode.set_attribute("up", "0")
|
||||
friendnode.add_child(Node.u32("code", friend.extid))
|
||||
friendnode.add_child(Node.string("name", friend.get_str("name")))
|
||||
friendnode.add_child(
|
||||
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in friend:
|
||||
title = Node.void('title')
|
||||
if "title" in friend:
|
||||
title = Node.void("title")
|
||||
friendnode.add_child(title)
|
||||
titledict = friend.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = friend.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in friend:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in friend:
|
||||
title_gr = Node.void("title_gr")
|
||||
friendnode.add_child(title_gr)
|
||||
title_grdict = friend.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = friend.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
friendnode.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
friendnode.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Target stuff
|
||||
target = Node.void('target')
|
||||
target = Node.void("target")
|
||||
root.add_child(target)
|
||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
||||
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||
|
||||
# Play area
|
||||
areas = profile.get_int_array('play_area', 55)
|
||||
play_area = Node.void('play_area')
|
||||
areas = profile.get_int_array("play_area", 55)
|
||||
play_area = Node.void("play_area")
|
||||
root.add_child(play_area)
|
||||
for i in range(len(areas)):
|
||||
play_area.set_attribute(f'play_cnt{i}', str(areas[i]))
|
||||
play_area.set_attribute(f"play_cnt{i}", str(areas[i]))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Grab last node and accessories so we can make decisions based on type
|
||||
last = request.child('last')
|
||||
lastdict = newprofile.get_dict('last')
|
||||
mode = int(last.attribute('mode'))
|
||||
style = int(last.attribute('style'))
|
||||
last = request.child("last")
|
||||
lastdict = newprofile.get_dict("last")
|
||||
mode = int(last.attribute("mode"))
|
||||
style = int(last.attribute("style"))
|
||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||
|
||||
# Drill rankings
|
||||
title = request.child('title')
|
||||
title_gr = request.child('title_gr')
|
||||
titledict = newprofile.get_dict('title')
|
||||
title_grdict = newprofile.get_dict('title_gr')
|
||||
title = request.child("title")
|
||||
title_gr = request.child("title_gr")
|
||||
titledict = newprofile.get_dict("title")
|
||||
title_grdict = newprofile.get_dict("title_gr")
|
||||
|
||||
# Groove radar level ups
|
||||
gr = request.child('gr')
|
||||
gr = request.child("gr")
|
||||
|
||||
# Set the correct values depending on if we're single or double play
|
||||
if is_dp:
|
||||
play_stats.increment_int('double_plays')
|
||||
play_stats.increment_int("double_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_d',
|
||||
"gr_d",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
title_grdict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
else:
|
||||
play_stats.increment_int('single_plays')
|
||||
play_stats.increment_int("single_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_s',
|
||||
"gr_s",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
play_stats.increment_int(f'cnt_m{mode}')
|
||||
title_grdict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
play_stats.increment_int(f"cnt_m{mode}")
|
||||
|
||||
# Result stars
|
||||
result_star = request.child('result_star')
|
||||
result_star = request.child("result_star")
|
||||
if result_star is not None:
|
||||
newprofile.replace_int_array(
|
||||
'result_stars',
|
||||
"result_stars",
|
||||
9,
|
||||
[
|
||||
intish(result_star.attribute('slot1')),
|
||||
intish(result_star.attribute('slot2')),
|
||||
intish(result_star.attribute('slot3')),
|
||||
intish(result_star.attribute('slot4')),
|
||||
intish(result_star.attribute('slot5')),
|
||||
intish(result_star.attribute('slot6')),
|
||||
intish(result_star.attribute('slot7')),
|
||||
intish(result_star.attribute('slot8')),
|
||||
intish(result_star.attribute('slot9')),
|
||||
intish(result_star.attribute("slot1")),
|
||||
intish(result_star.attribute("slot2")),
|
||||
intish(result_star.attribute("slot3")),
|
||||
intish(result_star.attribute("slot4")),
|
||||
intish(result_star.attribute("slot5")),
|
||||
intish(result_star.attribute("slot6")),
|
||||
intish(result_star.attribute("slot7")),
|
||||
intish(result_star.attribute("slot8")),
|
||||
intish(result_star.attribute("slot9")),
|
||||
],
|
||||
)
|
||||
|
||||
# Target stuff
|
||||
target = request.child('target')
|
||||
target = request.child("target")
|
||||
if target is not None:
|
||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
||||
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||
|
||||
# Update last attributes
|
||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Grab character options
|
||||
chara = request.child('chara')
|
||||
chara = request.child("chara")
|
||||
if chara is not None:
|
||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
||||
chara_opt = request.child('chara_opt')
|
||||
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||
chara_opt = request.child("chara_opt")
|
||||
if chara_opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('chara_opt', 96, chara_opt.value[:96])
|
||||
newprofile.replace_int_array("chara_opt", 96, chara_opt.value[:96])
|
||||
|
||||
# Options
|
||||
option_02 = request.child('option_02')
|
||||
option_02 = request.child("option_02")
|
||||
if option_02 is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('option_02', 24, option_02.value[:24])
|
||||
newprofile.replace_int_array("option_02", 24, option_02.value[:24])
|
||||
|
||||
# Experience and stars
|
||||
exp = request.child_value('exp')
|
||||
exp = request.child_value("exp")
|
||||
if exp is not None:
|
||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
||||
star = request.child_value('star')
|
||||
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||
star = request.child_value("star")
|
||||
if star is not None:
|
||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
||||
star_c = request.child_value('star_c')
|
||||
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||
star_c = request.child_value("star_c")
|
||||
if star_c is not None:
|
||||
newprofile.replace_int('star_c', newprofile.get_int('star_c') + exp)
|
||||
newprofile.replace_int("star_c", newprofile.get_int("star_c") + exp)
|
||||
|
||||
# Update game flags
|
||||
for child in request.children:
|
||||
if child.name not in ['flag', 'flag_ex']:
|
||||
if child.name not in ["flag", "flag_ex"]:
|
||||
continue
|
||||
try:
|
||||
value = int(child.attribute('data'))
|
||||
offset = int(child.attribute('no'))
|
||||
value = int(child.attribute("data"))
|
||||
offset = int(child.attribute("no"))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
flags = newprofile.get_int_array('flag', 512, [1] * 512)
|
||||
flags = newprofile.get_int_array("flag", 512, [1] * 512)
|
||||
if offset < 0 or offset >= len(flags):
|
||||
continue
|
||||
flags[offset] = value
|
||||
newprofile.replace_int_array('flag', 512, flags)
|
||||
newprofile.replace_int_array("flag", 512, flags)
|
||||
|
||||
# Workout mode support
|
||||
newweight = -1
|
||||
oldweight = newprofile.get_int('weight')
|
||||
oldweight = newprofile.get_int("weight")
|
||||
for child in request.children:
|
||||
if child.name != 'weight':
|
||||
if child.name != "weight":
|
||||
continue
|
||||
newweight = child.value
|
||||
if newweight < 0:
|
||||
@ -686,14 +714,14 @@ class DDR2014(
|
||||
# Either update or unset the weight depending on the game
|
||||
if newweight == 0:
|
||||
# Weight is unset or we declined to use this feature, remove from profile
|
||||
if 'weight' in newprofile:
|
||||
del newprofile['weight']
|
||||
if "weight" in newprofile:
|
||||
del newprofile["weight"]
|
||||
else:
|
||||
# Weight has been set or previously retrieved, we should save calories
|
||||
newprofile.replace_int('weight', newweight)
|
||||
newprofile.replace_int("weight", newweight)
|
||||
total = 0
|
||||
for child in request.children:
|
||||
if child.name != 'calory':
|
||||
if child.name != "calory":
|
||||
continue
|
||||
total += child.value
|
||||
self.data.local.user.put_time_based_achievement(
|
||||
@ -701,10 +729,10 @@ class DDR2014(
|
||||
self.version,
|
||||
userid,
|
||||
0,
|
||||
'workout',
|
||||
"workout",
|
||||
{
|
||||
'calories': total,
|
||||
'weight': newweight,
|
||||
"calories": total,
|
||||
"weight": newweight,
|
||||
},
|
||||
)
|
||||
|
||||
@ -712,7 +740,7 @@ class DDR2014(
|
||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
@ -721,11 +749,11 @@ class DDR2014(
|
||||
# Save any rivals that were added/removed/changed
|
||||
newfriends = oldfriends[:]
|
||||
for child in request.children:
|
||||
if child.name != 'friend':
|
||||
if child.name != "friend":
|
||||
continue
|
||||
|
||||
code = int(child.attribute('code'))
|
||||
pos = int(child.attribute('pos'))
|
||||
code = int(child.attribute("code"))
|
||||
pos = int(child.attribute("pos"))
|
||||
|
||||
if pos >= 0 and pos < 10:
|
||||
if code == 0:
|
||||
@ -733,7 +761,9 @@ class DDR2014(
|
||||
newfriends[pos] = None
|
||||
else:
|
||||
# Try looking up the userid
|
||||
newfriends[pos] = self.data.remote.user.from_extid(self.game, self.version, code)
|
||||
newfriends[pos] = self.data.remote.user.from_extid(
|
||||
self.game, self.version, code
|
||||
)
|
||||
|
||||
# Diff the set of links to determine updates
|
||||
for i in range(10):
|
||||
@ -746,7 +776,7 @@ class DDR2014(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
elif oldfriends[i] is None:
|
||||
@ -755,7 +785,7 @@ class DDR2014(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
@ -765,24 +795,24 @@ class DDR2014(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
self.data.local.user.put_link(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
|
||||
# Play area counter
|
||||
shop_area = int(request.attribute('shop_area'))
|
||||
shop_area = int(request.attribute("shop_area"))
|
||||
if shop_area >= 0 and shop_area < 55:
|
||||
areas = newprofile.get_int_array('play_area', 55)
|
||||
areas = newprofile.get_int_array("play_area", 55)
|
||||
areas[shop_area] = areas[shop_area] + 1
|
||||
newprofile.replace_int_array('play_area', 55, areas)
|
||||
newprofile.replace_int_array("play_area", 55, areas)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid, play_stats)
|
||||
|
@ -10,7 +10,7 @@ class DDRA20(
|
||||
DDRBase,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution A20'
|
||||
name: str = "DanceDanceRevolution A20"
|
||||
version: int = VersionConstants.DDR_A20
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -18,7 +18,7 @@ class DDRA20(
|
||||
|
||||
@property
|
||||
def supports_paseli(self) -> bool:
|
||||
if self.model.dest != 'J':
|
||||
if self.model.dest != "J":
|
||||
# DDR Ace in USA mode doesn't support PASELI properly.
|
||||
# When in Asia mode it shows PASELI but won't let you select it.
|
||||
return False
|
||||
|
@ -6,7 +6,15 @@ from typing_extensions import Final
|
||||
from bemani.backend.ess import EventLogHandler
|
||||
from bemani.backend.ddr.base import DDRBase
|
||||
from bemani.backend.ddr.ddr2014 import DDR2014
|
||||
from bemani.common import Profile, ValidatedDict, VersionConstants, CardCipher, Time, ID, intish
|
||||
from bemani.common import (
|
||||
Profile,
|
||||
ValidatedDict,
|
||||
VersionConstants,
|
||||
CardCipher,
|
||||
Time,
|
||||
ID,
|
||||
intish,
|
||||
)
|
||||
from bemani.data import Data, Achievement, Machine, Score, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -16,7 +24,7 @@ class DDRAce(
|
||||
EventLogHandler,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution A'
|
||||
name: str = "DanceDanceRevolution A"
|
||||
version: int = VersionConstants.DDR_ACE
|
||||
|
||||
GAME_STYLE_SINGLE: Final[int] = 0
|
||||
@ -106,7 +114,9 @@ class DDRAce(
|
||||
return DDR2014(self.data, self.config, self.model)
|
||||
|
||||
@classmethod
|
||||
def run_scheduled_work(cls, data: Data, config: Dict[str, Any]) -> List[Tuple[str, Dict[str, Any]]]:
|
||||
def run_scheduled_work(
|
||||
cls, data: Data, config: Dict[str, Any]
|
||||
) -> List[Tuple[str, Dict[str, Any]]]:
|
||||
# DDR Ace has a weird bug where it sends a profile save for a blank
|
||||
# profile before reading it back when creating a new profile. If there
|
||||
# is no profile on read-back, it errors out, and it also uses the name
|
||||
@ -125,20 +135,25 @@ class DDRAce(
|
||||
events = []
|
||||
|
||||
for userid, profile in profiles:
|
||||
if profile.get_str('name') == "" and profile.get_int('write_time') < several_minutes_ago:
|
||||
if (
|
||||
profile.get_str("name") == ""
|
||||
and profile.get_int("write_time") < several_minutes_ago
|
||||
):
|
||||
data.local.user.delete_profile(cls.game, cls.version, userid)
|
||||
events.append((
|
||||
'ddr_profile_purge',
|
||||
{
|
||||
'userid': userid,
|
||||
},
|
||||
))
|
||||
events.append(
|
||||
(
|
||||
"ddr_profile_purge",
|
||||
{
|
||||
"userid": userid,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
return events
|
||||
|
||||
@property
|
||||
def supports_paseli(self) -> bool:
|
||||
if self.model.dest != 'J':
|
||||
if self.model.dest != "J":
|
||||
# DDR Ace in USA mode doesn't support PASELI properly.
|
||||
# When in Asia mode it shows PASELI but won't let you select it.
|
||||
return False
|
||||
@ -237,19 +252,25 @@ class DDRAce(
|
||||
return self.GAME_HALO_NONE
|
||||
|
||||
def handle_tax_get_phase_request(self, request: Node) -> Node:
|
||||
tax = Node.void('tax')
|
||||
tax.add_child(Node.s32('phase', 0))
|
||||
tax = Node.void("tax")
|
||||
tax.add_child(Node.s32("phase", 0))
|
||||
return tax
|
||||
|
||||
def __handle_userload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
def __handle_userload(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
has_profile: bool = False
|
||||
achievements: List[Achievement] = []
|
||||
scores: List[Score] = []
|
||||
|
||||
if userid is not None:
|
||||
has_profile = self.has_profile(userid)
|
||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
achievements = self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
|
||||
# Place scores into an arrangement for easier distribution to Ace.
|
||||
scores_by_mcode: Dict[int, List[Optional[Score]]] = {}
|
||||
@ -260,34 +281,43 @@ class DDRAce(
|
||||
scores_by_mcode[score.id][self.db_to_game_chart(score.chart)] = score
|
||||
|
||||
# First, set new flag
|
||||
response.add_child(Node.bool('is_new', not has_profile))
|
||||
response.add_child(Node.bool("is_new", not has_profile))
|
||||
|
||||
# Now, return the scores to Ace
|
||||
for mcode in scores_by_mcode:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
response.add_child(music)
|
||||
music.add_child(Node.u32('mcode', mcode))
|
||||
music.add_child(Node.u32("mcode", mcode))
|
||||
|
||||
scores_that_matter = scores_by_mcode[mcode]
|
||||
while scores_that_matter[-1] is None:
|
||||
scores_that_matter = scores_that_matter[:-1]
|
||||
|
||||
for score in scores_that_matter:
|
||||
note = Node.void('note')
|
||||
note = Node.void("note")
|
||||
music.add_child(note)
|
||||
|
||||
if score is None:
|
||||
note.add_child(Node.u16('count', 0))
|
||||
note.add_child(Node.u8('rank', 0))
|
||||
note.add_child(Node.u8('clearkind', 0))
|
||||
note.add_child(Node.s32('score', 0))
|
||||
note.add_child(Node.s32('ghostid', 0))
|
||||
note.add_child(Node.u16("count", 0))
|
||||
note.add_child(Node.u8("rank", 0))
|
||||
note.add_child(Node.u8("clearkind", 0))
|
||||
note.add_child(Node.s32("score", 0))
|
||||
note.add_child(Node.s32("ghostid", 0))
|
||||
else:
|
||||
note.add_child(Node.u16('count', score.plays))
|
||||
note.add_child(Node.u8('rank', self.db_to_game_rank(score.data.get_int('rank'))))
|
||||
note.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo'))))
|
||||
note.add_child(Node.s32('score', score.points))
|
||||
note.add_child(Node.s32('ghostid', score.key))
|
||||
note.add_child(Node.u16("count", score.plays))
|
||||
note.add_child(
|
||||
Node.u8(
|
||||
"rank", self.db_to_game_rank(score.data.get_int("rank"))
|
||||
)
|
||||
)
|
||||
note.add_child(
|
||||
Node.u8(
|
||||
"clearkind",
|
||||
self.db_to_game_halo(score.data.get_int("halo")),
|
||||
)
|
||||
)
|
||||
note.add_child(Node.s32("score", score.points))
|
||||
note.add_child(Node.s32("ghostid", score.key))
|
||||
|
||||
# Active event settings
|
||||
activeevents = [
|
||||
@ -331,69 +361,83 @@ class DDRAce(
|
||||
|
||||
# Event reward settings
|
||||
rewards = {
|
||||
'30': {
|
||||
"30": {
|
||||
999: 5,
|
||||
}
|
||||
}
|
||||
|
||||
# Now handle event progress and activation.
|
||||
events = {ach.id: ach.data for ach in achievements if ach.type == '9999'}
|
||||
progress = [ach for ach in achievements if ach.type != '9999']
|
||||
events = {ach.id: ach.data for ach in achievements if ach.type == "9999"}
|
||||
progress = [ach for ach in achievements if ach.type != "9999"]
|
||||
|
||||
# Make sure we always send a babylon's adventure save event or the game won't send progress
|
||||
babylon_included = False
|
||||
for evtprogress in progress:
|
||||
if evtprogress.id == 999 and evtprogress.type == '30':
|
||||
if evtprogress.id == 999 and evtprogress.type == "30":
|
||||
babylon_included = True
|
||||
break
|
||||
|
||||
if not babylon_included:
|
||||
progress.append(Achievement(
|
||||
999,
|
||||
'30',
|
||||
None,
|
||||
{
|
||||
'completed': False,
|
||||
'progress': 0,
|
||||
},
|
||||
))
|
||||
progress.append(
|
||||
Achievement(
|
||||
999,
|
||||
"30",
|
||||
None,
|
||||
{
|
||||
"completed": False,
|
||||
"progress": 0,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
for event in activeevents:
|
||||
# Get completion data
|
||||
playerstats = events.get(event, ValidatedDict({'completed': False}))
|
||||
playerstats = events.get(event, ValidatedDict({"completed": False}))
|
||||
|
||||
# Return the data
|
||||
eventdata = Node.void('eventdata')
|
||||
eventdata = Node.void("eventdata")
|
||||
response.add_child(eventdata)
|
||||
eventdata.add_child(Node.u32('eventid', event))
|
||||
eventdata.add_child(Node.s32('eventtype', 9999))
|
||||
eventdata.add_child(Node.u32('eventno', 0))
|
||||
eventdata.add_child(Node.s64('condition', 0))
|
||||
eventdata.add_child(Node.u32('reward', 0))
|
||||
eventdata.add_child(Node.s32('comptime', 1 if playerstats.get_bool('completed') else 0))
|
||||
eventdata.add_child(Node.s64('savedata', 0))
|
||||
eventdata.add_child(Node.u32("eventid", event))
|
||||
eventdata.add_child(Node.s32("eventtype", 9999))
|
||||
eventdata.add_child(Node.u32("eventno", 0))
|
||||
eventdata.add_child(Node.s64("condition", 0))
|
||||
eventdata.add_child(Node.u32("reward", 0))
|
||||
eventdata.add_child(
|
||||
Node.s32("comptime", 1 if playerstats.get_bool("completed") else 0)
|
||||
)
|
||||
eventdata.add_child(Node.s64("savedata", 0))
|
||||
|
||||
for evtprogress in progress:
|
||||
# Babylon's adventure progres and anything else the game sends
|
||||
eventdata = Node.void('eventdata')
|
||||
eventdata = Node.void("eventdata")
|
||||
response.add_child(eventdata)
|
||||
eventdata.add_child(Node.u32('eventid', evtprogress.id))
|
||||
eventdata.add_child(Node.s32('eventtype', int(evtprogress.type)))
|
||||
eventdata.add_child(Node.u32('eventno', 0))
|
||||
eventdata.add_child(Node.s64('condition', 0))
|
||||
eventdata.add_child(Node.u32('reward', rewards.get(evtprogress.type, {}).get(evtprogress.id)))
|
||||
eventdata.add_child(Node.s32('comptime', 1 if evtprogress.data.get_bool('completed') else 0))
|
||||
eventdata.add_child(Node.s64('savedata', evtprogress.data.get_int('progress')))
|
||||
eventdata.add_child(Node.u32("eventid", evtprogress.id))
|
||||
eventdata.add_child(Node.s32("eventtype", int(evtprogress.type)))
|
||||
eventdata.add_child(Node.u32("eventno", 0))
|
||||
eventdata.add_child(Node.s64("condition", 0))
|
||||
eventdata.add_child(
|
||||
Node.u32(
|
||||
"reward", rewards.get(evtprogress.type, {}).get(evtprogress.id)
|
||||
)
|
||||
)
|
||||
eventdata.add_child(
|
||||
Node.s32("comptime", 1 if evtprogress.data.get_bool("completed") else 0)
|
||||
)
|
||||
eventdata.add_child(
|
||||
Node.s64("savedata", evtprogress.data.get_int("progress"))
|
||||
)
|
||||
|
||||
def __handle_usersave(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
def __handle_usersave(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
if userid is None:
|
||||
# the game sends us empty user ID strings when a guest is playing.
|
||||
# Return early so it doesn't wait a minute and a half to show the
|
||||
# results screen.
|
||||
return
|
||||
|
||||
if requestdata.child_value('isgameover'):
|
||||
style = int(requestdata.child_value('playstyle'))
|
||||
if requestdata.child_value("isgameover"):
|
||||
style = int(requestdata.child_value("playstyle"))
|
||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||
|
||||
# We don't save anything for gameover requests, since we
|
||||
@ -401,52 +445,52 @@ class DDRAce(
|
||||
# as a spot to bump play counts and such
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
if is_dp:
|
||||
play_stats.increment_int('double_plays')
|
||||
play_stats.increment_int("double_plays")
|
||||
else:
|
||||
play_stats.increment_int('single_plays')
|
||||
play_stats.increment_int("single_plays")
|
||||
self.update_play_statistics(userid, play_stats)
|
||||
|
||||
# Now is a good time to check if we have workout mode enabled,
|
||||
# and if so, store the calories earned for this set.
|
||||
profile = self.get_profile(userid)
|
||||
enabled = profile.get_bool('workout_mode')
|
||||
weight = profile.get_int('weight')
|
||||
enabled = profile.get_bool("workout_mode")
|
||||
weight = profile.get_int("weight")
|
||||
|
||||
if enabled and weight > 0:
|
||||
# We enabled weight display, find the calories and save them
|
||||
total = 0
|
||||
for child in requestdata.children:
|
||||
if child.name != 'note':
|
||||
if child.name != "note":
|
||||
continue
|
||||
|
||||
total = total + (child.child_value('calorie') or 0)
|
||||
total = total + (child.child_value("calorie") or 0)
|
||||
|
||||
self.data.local.user.put_time_based_achievement(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
0,
|
||||
'workout',
|
||||
"workout",
|
||||
{
|
||||
'calories': total,
|
||||
'weight': weight,
|
||||
"calories": total,
|
||||
"weight": weight,
|
||||
},
|
||||
)
|
||||
|
||||
# Find any event updates
|
||||
for child in requestdata.children:
|
||||
if child.name != 'event':
|
||||
if child.name != "event":
|
||||
continue
|
||||
|
||||
# Skip empty events or events we don't support
|
||||
eventid = child.child_value('eventid')
|
||||
eventtype = child.child_value('eventtype')
|
||||
eventid = child.child_value("eventid")
|
||||
eventtype = child.child_value("eventtype")
|
||||
if eventid == 0 or eventtype == 0:
|
||||
continue
|
||||
|
||||
# Save data to replay to the client later
|
||||
completed = child.child_value('comptime') != 0
|
||||
progress = child.child_value('savedata')
|
||||
completed = child.child_value("comptime") != 0
|
||||
progress = child.child_value("savedata")
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
self.game,
|
||||
@ -455,8 +499,8 @@ class DDRAce(
|
||||
eventid,
|
||||
str(eventtype),
|
||||
{
|
||||
'completed': completed,
|
||||
'progress': progress,
|
||||
"completed": completed,
|
||||
"progress": progress,
|
||||
},
|
||||
)
|
||||
|
||||
@ -466,23 +510,23 @@ class DDRAce(
|
||||
score = None
|
||||
stagenum = 0
|
||||
for child in requestdata.children:
|
||||
if child.name != 'note':
|
||||
if child.name != "note":
|
||||
continue
|
||||
|
||||
if child.child_value('stagenum') > stagenum:
|
||||
if child.child_value("stagenum") > stagenum:
|
||||
score = child
|
||||
stagenum = child.child_value('stagenum')
|
||||
stagenum = child.child_value("stagenum")
|
||||
|
||||
if score is None:
|
||||
raise Exception('Couldn\'t find newest score to save!')
|
||||
raise Exception("Couldn't find newest score to save!")
|
||||
|
||||
songid = score.child_value('mcode')
|
||||
chart = self.game_to_db_chart(score.child_value('notetype'))
|
||||
rank = self.game_to_db_rank(score.child_value('rank'))
|
||||
halo = self.game_to_db_halo(score.child_value('clearkind'))
|
||||
points = score.child_value('score')
|
||||
combo = score.child_value('maxcombo')
|
||||
ghost = score.child_value('ghost')
|
||||
songid = score.child_value("mcode")
|
||||
chart = self.game_to_db_chart(score.child_value("notetype"))
|
||||
rank = self.game_to_db_rank(score.child_value("rank"))
|
||||
halo = self.game_to_db_halo(score.child_value("clearkind"))
|
||||
points = score.child_value("score")
|
||||
combo = score.child_value("maxcombo")
|
||||
ghost = score.child_value("ghost")
|
||||
self.update_score(
|
||||
userid,
|
||||
songid,
|
||||
@ -494,15 +538,17 @@ class DDRAce(
|
||||
ghost=ghost,
|
||||
)
|
||||
|
||||
def __handle_rivalload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
data = Node.void('data')
|
||||
def __handle_rivalload(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
data = Node.void("data")
|
||||
response.add_child(data)
|
||||
data.add_child(Node.s32('recordtype', requestdata.child_value('loadflag')))
|
||||
data.add_child(Node.s32("recordtype", requestdata.child_value("loadflag")))
|
||||
|
||||
thismachine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||
machines_by_id: Dict[int, Optional[Machine]] = {thismachine.id: thismachine}
|
||||
|
||||
loadkind = requestdata.child_value('loadflag')
|
||||
loadkind = requestdata.child_value("loadflag")
|
||||
profiles_by_userid: Dict[UserID, Profile] = {}
|
||||
|
||||
def get_machine(lid: int) -> Optional[Machine]:
|
||||
@ -522,7 +568,9 @@ class DDRAce(
|
||||
|
||||
if loadkind == self.GAME_RIVAL_TYPE_WORLD:
|
||||
# Just load all scores for this network
|
||||
scores = self.data.remote.music.get_all_records(self.game, self.music_version)
|
||||
scores = self.data.remote.music.get_all_records(
|
||||
self.game, self.music_version
|
||||
)
|
||||
elif loadkind == self.GAME_RIVAL_TYPE_AREA:
|
||||
if thismachine.arcade is not None:
|
||||
match_arcade = thismachine.arcade
|
||||
@ -540,57 +588,71 @@ class DDRAce(
|
||||
# If we have an arcade to match, see if this user's location matches the arcade.
|
||||
# If we don't, just match lid directly
|
||||
if match_arcade is not None:
|
||||
theirmachine = get_machine(profiledata.get_int('lid'))
|
||||
theirmachine = get_machine(profiledata.get_int("lid"))
|
||||
if theirmachine is not None and theirmachine.arcade == match_arcade:
|
||||
userids.append(userid)
|
||||
elif match_machine is not None:
|
||||
if profiledata.get_int('lid') == match_machine:
|
||||
if profiledata.get_int("lid") == match_machine:
|
||||
userids.append(userid)
|
||||
|
||||
# Load all scores for users in the area
|
||||
scores = self.data.local.music.get_all_records(self.game, self.music_version, userlist=userids)
|
||||
scores = self.data.local.music.get_all_records(
|
||||
self.game, self.music_version, userlist=userids
|
||||
)
|
||||
elif loadkind == self.GAME_RIVAL_TYPE_MACHINE:
|
||||
# Load up all scores and filter them by those earned at this location
|
||||
scores = self.data.local.music.get_all_records(self.game, self.music_version, locationlist=[thismachine.id])
|
||||
scores = self.data.local.music.get_all_records(
|
||||
self.game, self.music_version, locationlist=[thismachine.id]
|
||||
)
|
||||
elif loadkind in [
|
||||
self.GAME_RIVAL_TYPE_RIVAL1,
|
||||
self.GAME_RIVAL_TYPE_RIVAL2,
|
||||
self.GAME_RIVAL_TYPE_RIVAL3,
|
||||
]:
|
||||
# Load up this user's highscores, format the way the below code expects it
|
||||
extid = requestdata.child_value('ddrcode')
|
||||
extid = requestdata.child_value("ddrcode")
|
||||
otherid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
userscores = self.data.remote.music.get_scores(self.game, self.music_version, otherid)
|
||||
userscores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, otherid
|
||||
)
|
||||
scores = [(otherid, score) for score in userscores]
|
||||
else:
|
||||
# Nothing here
|
||||
scores = []
|
||||
|
||||
missing_users = [userid for (userid, _) in scores if userid not in profiles_by_userid]
|
||||
missing_users = [
|
||||
userid for (userid, _) in scores if userid not in profiles_by_userid
|
||||
]
|
||||
for (userid, profile) in self.get_any_profiles(missing_users):
|
||||
profiles_by_userid[userid] = profile
|
||||
|
||||
for userid, score in scores:
|
||||
if profiles_by_userid.get(userid) is None:
|
||||
raise Exception(f'Logic error, couldn\'t find any profile for {userid}')
|
||||
raise Exception(f"Logic error, couldn't find any profile for {userid}")
|
||||
profiledata = profiles_by_userid[userid]
|
||||
|
||||
record = Node.void('record')
|
||||
record = Node.void("record")
|
||||
data.add_child(record)
|
||||
record.add_child(Node.u32('mcode', score.id))
|
||||
record.add_child(Node.u8('notetype', self.db_to_game_chart(score.chart)))
|
||||
record.add_child(Node.u8('rank', self.db_to_game_rank(score.data.get_int('rank'))))
|
||||
record.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo'))))
|
||||
record.add_child(Node.u8('flagdata', 0))
|
||||
record.add_child(Node.string('name', profiledata.get_str('name')))
|
||||
record.add_child(Node.s32('area', profiledata.get_int('area', 58)))
|
||||
record.add_child(Node.s32('code', profiledata.extid))
|
||||
record.add_child(Node.s32('score', score.points))
|
||||
record.add_child(Node.s32('ghostid', score.key))
|
||||
record.add_child(Node.u32("mcode", score.id))
|
||||
record.add_child(Node.u8("notetype", self.db_to_game_chart(score.chart)))
|
||||
record.add_child(
|
||||
Node.u8("rank", self.db_to_game_rank(score.data.get_int("rank")))
|
||||
)
|
||||
record.add_child(
|
||||
Node.u8("clearkind", self.db_to_game_halo(score.data.get_int("halo")))
|
||||
)
|
||||
record.add_child(Node.u8("flagdata", 0))
|
||||
record.add_child(Node.string("name", profiledata.get_str("name")))
|
||||
record.add_child(Node.s32("area", profiledata.get_int("area", 58)))
|
||||
record.add_child(Node.s32("code", profiledata.extid))
|
||||
record.add_child(Node.s32("score", score.points))
|
||||
record.add_child(Node.s32("ghostid", score.key))
|
||||
|
||||
def __handle_usernew(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
def __handle_usernew(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
if userid is None:
|
||||
raise Exception('Expecting valid UserID to create new profile!')
|
||||
raise Exception("Expecting valid UserID to create new profile!")
|
||||
|
||||
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||
profile = Profile(
|
||||
@ -599,27 +661,35 @@ class DDRAce(
|
||||
"",
|
||||
0,
|
||||
{
|
||||
'lid': machine.id,
|
||||
"lid": machine.id,
|
||||
},
|
||||
)
|
||||
self.put_profile(userid, profile)
|
||||
|
||||
response.add_child(Node.string('seq', ID.format_extid(profile.extid)))
|
||||
response.add_child(Node.s32('code', profile.extid))
|
||||
response.add_child(Node.string('shoparea', ''))
|
||||
response.add_child(Node.string("seq", ID.format_extid(profile.extid)))
|
||||
response.add_child(Node.s32("code", profile.extid))
|
||||
response.add_child(Node.string("shoparea", ""))
|
||||
|
||||
def __handle_inheritance(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
def __handle_inheritance(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
if userid is not None:
|
||||
previous_version = self.previous_version()
|
||||
profile = previous_version.get_profile(userid)
|
||||
else:
|
||||
profile = None
|
||||
|
||||
response.add_child(Node.s32('InheritanceStatus', 1 if profile is not None else 0))
|
||||
response.add_child(
|
||||
Node.s32("InheritanceStatus", 1 if profile is not None else 0)
|
||||
)
|
||||
|
||||
def __handle_ghostload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
||||
ghostid = requestdata.child_value('ghostid')
|
||||
ghost = self.data.local.music.get_score_by_key(self.game, self.music_version, ghostid)
|
||||
def __handle_ghostload(
|
||||
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||
) -> None:
|
||||
ghostid = requestdata.child_value("ghostid")
|
||||
ghost = self.data.local.music.get_score_by_key(
|
||||
self.game, self.music_version, ghostid
|
||||
)
|
||||
if ghost is None:
|
||||
return
|
||||
|
||||
@ -628,110 +698,188 @@ class DDRAce(
|
||||
if profile is None:
|
||||
return
|
||||
|
||||
if 'ghost' not in score.data:
|
||||
if "ghost" not in score.data:
|
||||
return
|
||||
|
||||
ghostdata = Node.void('ghostdata')
|
||||
ghostdata = Node.void("ghostdata")
|
||||
response.add_child(ghostdata)
|
||||
ghostdata.add_child(Node.s32('code', profile.extid))
|
||||
ghostdata.add_child(Node.u32('mcode', score.id))
|
||||
ghostdata.add_child(Node.u8('notetype', self.db_to_game_chart(score.chart)))
|
||||
ghostdata.add_child(Node.s32('ghostsize', len(score.data['ghost'])))
|
||||
ghostdata.add_child(Node.string('ghost', score.data['ghost']))
|
||||
ghostdata.add_child(Node.s32("code", profile.extid))
|
||||
ghostdata.add_child(Node.u32("mcode", score.id))
|
||||
ghostdata.add_child(Node.u8("notetype", self.db_to_game_chart(score.chart)))
|
||||
ghostdata.add_child(Node.s32("ghostsize", len(score.data["ghost"])))
|
||||
ghostdata.add_child(Node.string("ghost", score.data["ghost"]))
|
||||
|
||||
def handle_playerdata_usergamedata_advanced_request(self, request: Node) -> Optional[Node]:
|
||||
playerdata = Node.void('playerdata')
|
||||
def handle_playerdata_usergamedata_advanced_request(
|
||||
self, request: Node
|
||||
) -> Optional[Node]:
|
||||
playerdata = Node.void("playerdata")
|
||||
|
||||
# DDR Ace decides to be difficult and have a third level of packet switching
|
||||
mode = request.child_value('data/mode')
|
||||
refid = request.child_value('data/refid')
|
||||
extid = request.child_value('data/ddrcode')
|
||||
mode = request.child_value("data/mode")
|
||||
refid = request.child_value("data/refid")
|
||||
extid = request.child_value("data/ddrcode")
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
# Possibly look up by extid instead
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
|
||||
if mode == 'userload':
|
||||
self.__handle_userload(userid, request.child('data'), playerdata)
|
||||
elif mode == 'usersave':
|
||||
self.__handle_usersave(userid, request.child('data'), playerdata)
|
||||
elif mode == 'rivalload':
|
||||
self.__handle_rivalload(userid, request.child('data'), playerdata)
|
||||
elif mode == 'usernew':
|
||||
self.__handle_usernew(userid, request.child('data'), playerdata)
|
||||
elif mode == 'inheritance':
|
||||
self.__handle_inheritance(userid, request.child('data'), playerdata)
|
||||
elif mode == 'ghostload':
|
||||
self.__handle_ghostload(userid, request.child('data'), playerdata)
|
||||
if mode == "userload":
|
||||
self.__handle_userload(userid, request.child("data"), playerdata)
|
||||
elif mode == "usersave":
|
||||
self.__handle_usersave(userid, request.child("data"), playerdata)
|
||||
elif mode == "rivalload":
|
||||
self.__handle_rivalload(userid, request.child("data"), playerdata)
|
||||
elif mode == "usernew":
|
||||
self.__handle_usernew(userid, request.child("data"), playerdata)
|
||||
elif mode == "inheritance":
|
||||
self.__handle_inheritance(userid, request.child("data"), playerdata)
|
||||
elif mode == "ghostload":
|
||||
self.__handle_ghostload(userid, request.child("data"), playerdata)
|
||||
else:
|
||||
# We don't support this
|
||||
return None
|
||||
|
||||
playerdata.add_child(Node.s32('result', 0))
|
||||
playerdata.add_child(Node.s32("result", 0))
|
||||
return playerdata
|
||||
|
||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||
playerdata = Node.void('playerdata')
|
||||
refid = request.child_value('data/refid')
|
||||
playerdata = Node.void("playerdata")
|
||||
refid = request.child_value("data/refid")
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
||||
usergamedata = profile.get_dict('usergamedata')
|
||||
profile = self.get_profile(userid) or Profile(
|
||||
self.game, self.version, refid, 0
|
||||
)
|
||||
usergamedata = profile.get_dict("usergamedata")
|
||||
|
||||
for record in request.child('data/record').children:
|
||||
if record.name != 'd':
|
||||
for record in request.child("data/record").children:
|
||||
if record.name != "d":
|
||||
continue
|
||||
|
||||
strdata = base64.b64decode(record.value)
|
||||
bindata = base64.b64decode(record.child_value('bin1'))
|
||||
bindata = base64.b64decode(record.child_value("bin1"))
|
||||
|
||||
# Grab and format the profile objects
|
||||
strdatalist = strdata.split(b',')
|
||||
profiletype = strdatalist[1].decode('utf-8')
|
||||
strdatalist = strdata.split(b",")
|
||||
profiletype = strdatalist[1].decode("utf-8")
|
||||
strdatalist = strdatalist[2:]
|
||||
|
||||
# Extract relevant bits for frontend/API
|
||||
if profiletype == 'COMMON':
|
||||
profile.replace_str('name', strdatalist[self.GAME_COMMON_NAME_OFFSET].decode('ascii'))
|
||||
profile.replace_int('area', intish(strdatalist[self.GAME_COMMON_AREA_OFFSET].decode('ascii'), 16))
|
||||
profile.replace_bool('workout_mode', int(strdatalist[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET].decode('ascii'), 16) != 0)
|
||||
profile.replace_int('weight', int(float(strdatalist[self.GAME_COMMON_WEIGHT_OFFSET].decode('ascii')) * 10))
|
||||
profile.replace_int('character', int(strdatalist[self.GAME_COMMON_CHARACTER_OFFSET].decode('ascii'), 16))
|
||||
if profiletype == 'OPTION':
|
||||
profile.replace_int('combo', int(strdatalist[self.GAME_OPTION_COMBO_POSITION_OFFSET].decode('ascii'), 16))
|
||||
profile.replace_int('early_late', int(strdatalist[self.GAME_OPTION_FAST_SLOW_OFFSET].decode('ascii'), 16))
|
||||
profile.replace_int('arrowskin', int(strdatalist[self.GAME_OPTION_ARROW_SKIN_OFFSET].decode('ascii'), 16))
|
||||
profile.replace_int('guidelines', int(strdatalist[self.GAME_OPTION_GUIDELINE_OFFSET].decode('ascii'), 16))
|
||||
profile.replace_int('filter', int(strdatalist[self.GAME_OPTION_FILTER_OFFSET].decode('ascii'), 16))
|
||||
if profiletype == "COMMON":
|
||||
profile.replace_str(
|
||||
"name",
|
||||
strdatalist[self.GAME_COMMON_NAME_OFFSET].decode("ascii"),
|
||||
)
|
||||
profile.replace_int(
|
||||
"area",
|
||||
intish(
|
||||
strdatalist[self.GAME_COMMON_AREA_OFFSET].decode("ascii"),
|
||||
16,
|
||||
),
|
||||
)
|
||||
profile.replace_bool(
|
||||
"workout_mode",
|
||||
int(
|
||||
strdatalist[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
)
|
||||
!= 0,
|
||||
)
|
||||
profile.replace_int(
|
||||
"weight",
|
||||
int(
|
||||
float(
|
||||
strdatalist[self.GAME_COMMON_WEIGHT_OFFSET].decode(
|
||||
"ascii"
|
||||
)
|
||||
)
|
||||
* 10
|
||||
),
|
||||
)
|
||||
profile.replace_int(
|
||||
"character",
|
||||
int(
|
||||
strdatalist[self.GAME_COMMON_CHARACTER_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
),
|
||||
)
|
||||
if profiletype == "OPTION":
|
||||
profile.replace_int(
|
||||
"combo",
|
||||
int(
|
||||
strdatalist[self.GAME_OPTION_COMBO_POSITION_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
),
|
||||
)
|
||||
profile.replace_int(
|
||||
"early_late",
|
||||
int(
|
||||
strdatalist[self.GAME_OPTION_FAST_SLOW_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
),
|
||||
)
|
||||
profile.replace_int(
|
||||
"arrowskin",
|
||||
int(
|
||||
strdatalist[self.GAME_OPTION_ARROW_SKIN_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
),
|
||||
)
|
||||
profile.replace_int(
|
||||
"guidelines",
|
||||
int(
|
||||
strdatalist[self.GAME_OPTION_GUIDELINE_OFFSET].decode(
|
||||
"ascii"
|
||||
),
|
||||
16,
|
||||
),
|
||||
)
|
||||
profile.replace_int(
|
||||
"filter",
|
||||
int(
|
||||
strdatalist[self.GAME_OPTION_FILTER_OFFSET].decode("ascii"),
|
||||
16,
|
||||
),
|
||||
)
|
||||
|
||||
usergamedata[profiletype] = {
|
||||
'strdata': b','.join(strdatalist),
|
||||
'bindata': bindata,
|
||||
"strdata": b",".join(strdatalist),
|
||||
"bindata": bindata,
|
||||
}
|
||||
|
||||
profile.replace_dict('usergamedata', usergamedata)
|
||||
profile.replace_int('write_time', Time.now())
|
||||
profile.replace_dict("usergamedata", usergamedata)
|
||||
profile.replace_int("write_time", Time.now())
|
||||
self.put_profile(userid, profile)
|
||||
|
||||
playerdata.add_child(Node.s32('result', 0))
|
||||
playerdata.add_child(Node.s32("result", 0))
|
||||
return playerdata
|
||||
|
||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||
playerdata = Node.void('playerdata')
|
||||
playerdata = Node.void("playerdata")
|
||||
|
||||
player = Node.void('player')
|
||||
player = Node.void("player")
|
||||
playerdata.add_child(player)
|
||||
|
||||
refid = request.child_value('data/refid')
|
||||
refid = request.child_value("data/refid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid)
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
records = 0
|
||||
|
||||
record = Node.void('record')
|
||||
record = Node.void("record")
|
||||
player.add_child(record)
|
||||
|
||||
def acehex(val: int) -> str:
|
||||
@ -739,70 +887,92 @@ class DDRAce(
|
||||
|
||||
if profile is None:
|
||||
# Just return a default empty node
|
||||
record.add_child(Node.string('d', '<NODATA>'))
|
||||
record.add_child(Node.string("d", "<NODATA>"))
|
||||
records = 1
|
||||
else:
|
||||
# Figure out what profiles are being requested
|
||||
profiletypes = request.child_value('data/recv_csv').split(',')[::2]
|
||||
usergamedata = profile.get_dict('usergamedata')
|
||||
profiletypes = request.child_value("data/recv_csv").split(",")[::2]
|
||||
usergamedata = profile.get_dict("usergamedata")
|
||||
for ptype in profiletypes:
|
||||
if ptype in usergamedata:
|
||||
records = records + 1
|
||||
|
||||
if ptype == "COMMON":
|
||||
# Return basic profile options
|
||||
name = profile.get_str('name')
|
||||
area = profile.get_int('area', self.get_machine_region())
|
||||
name = profile.get_str("name")
|
||||
area = profile.get_int("area", self.get_machine_region())
|
||||
if name == "":
|
||||
# This is a bogus profile created by the first login, substitute the
|
||||
# previous version values so that profile succession works.
|
||||
previous_version = self.previous_version()
|
||||
old_profile = previous_version.get_profile(userid)
|
||||
if old_profile is not None:
|
||||
name = old_profile.get_str('name')
|
||||
area = old_profile.get_int('area', self.get_machine_region())
|
||||
name = old_profile.get_str("name")
|
||||
area = old_profile.get_int(
|
||||
"area", self.get_machine_region()
|
||||
)
|
||||
else:
|
||||
area = self.get_machine_region()
|
||||
|
||||
common = usergamedata[ptype]['strdata'].split(b',')
|
||||
common[self.GAME_COMMON_NAME_OFFSET] = name.encode('ascii')
|
||||
common[self.GAME_COMMON_AREA_OFFSET] = acehex(area).encode('ascii')
|
||||
common[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET] = b'1' if profile.get_bool('workout_mode') else b'0'
|
||||
common[self.GAME_COMMON_WEIGHT_OFFSET] = str(float(profile.get_int('weight')) / 10.0).encode('ascii')
|
||||
common[self.GAME_COMMON_CHARACTER_OFFSET] = acehex(profile.get_int('character')).encode('ascii')
|
||||
usergamedata[ptype]['strdata'] = b','.join(common)
|
||||
common = usergamedata[ptype]["strdata"].split(b",")
|
||||
common[self.GAME_COMMON_NAME_OFFSET] = name.encode("ascii")
|
||||
common[self.GAME_COMMON_AREA_OFFSET] = acehex(area).encode(
|
||||
"ascii"
|
||||
)
|
||||
common[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET] = (
|
||||
b"1" if profile.get_bool("workout_mode") else b"0"
|
||||
)
|
||||
common[self.GAME_COMMON_WEIGHT_OFFSET] = str(
|
||||
float(profile.get_int("weight")) / 10.0
|
||||
).encode("ascii")
|
||||
common[self.GAME_COMMON_CHARACTER_OFFSET] = acehex(
|
||||
profile.get_int("character")
|
||||
).encode("ascii")
|
||||
usergamedata[ptype]["strdata"] = b",".join(common)
|
||||
if ptype == "OPTION":
|
||||
# Return user settings for frontend
|
||||
option = usergamedata[ptype]['strdata'].split(b',')
|
||||
option[self.GAME_OPTION_FAST_SLOW_OFFSET] = acehex(profile.get_int('early_late')).encode('ascii')
|
||||
option[self.GAME_OPTION_COMBO_POSITION_OFFSET] = acehex(profile.get_int('combo')).encode('ascii')
|
||||
option[self.GAME_OPTION_ARROW_SKIN_OFFSET] = acehex(profile.get_int('arrowskin')).encode('ascii')
|
||||
option[self.GAME_OPTION_GUIDELINE_OFFSET] = acehex(profile.get_int('guidelines')).encode('ascii')
|
||||
option[self.GAME_OPTION_FILTER_OFFSET] = acehex(profile.get_int('filter')).encode('ascii')
|
||||
usergamedata[ptype]['strdata'] = b','.join(option)
|
||||
option = usergamedata[ptype]["strdata"].split(b",")
|
||||
option[self.GAME_OPTION_FAST_SLOW_OFFSET] = acehex(
|
||||
profile.get_int("early_late")
|
||||
).encode("ascii")
|
||||
option[self.GAME_OPTION_COMBO_POSITION_OFFSET] = acehex(
|
||||
profile.get_int("combo")
|
||||
).encode("ascii")
|
||||
option[self.GAME_OPTION_ARROW_SKIN_OFFSET] = acehex(
|
||||
profile.get_int("arrowskin")
|
||||
).encode("ascii")
|
||||
option[self.GAME_OPTION_GUIDELINE_OFFSET] = acehex(
|
||||
profile.get_int("guidelines")
|
||||
).encode("ascii")
|
||||
option[self.GAME_OPTION_FILTER_OFFSET] = acehex(
|
||||
profile.get_int("filter")
|
||||
).encode("ascii")
|
||||
usergamedata[ptype]["strdata"] = b",".join(option)
|
||||
if ptype == "LAST":
|
||||
# Return the number of calories expended in the last day
|
||||
workouts = self.data.local.user.get_time_based_achievements(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
achievementtype='workout',
|
||||
achievementtype="workout",
|
||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
||||
)
|
||||
total = sum([w.data.get_int('calories') for w in workouts])
|
||||
total = sum([w.data.get_int("calories") for w in workouts])
|
||||
|
||||
last = usergamedata[ptype]['strdata'].split(b',')
|
||||
last[self.GAME_LAST_CALORIES_OFFSET] = acehex(total).encode('ascii')
|
||||
usergamedata[ptype]['strdata'] = b','.join(last)
|
||||
last = usergamedata[ptype]["strdata"].split(b",")
|
||||
last[self.GAME_LAST_CALORIES_OFFSET] = acehex(total).encode(
|
||||
"ascii"
|
||||
)
|
||||
usergamedata[ptype]["strdata"] = b",".join(last)
|
||||
if ptype == "RIVAL":
|
||||
# Fill in the DDR code and active status of the three active
|
||||
# rivals.
|
||||
rival = usergamedata[ptype]['strdata'].split(b',')
|
||||
lastdict = profile.get_dict('last')
|
||||
rival = usergamedata[ptype]["strdata"].split(b",")
|
||||
lastdict = profile.get_dict("last")
|
||||
|
||||
friends: Dict[int, Optional[Profile]] = {}
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
@ -815,16 +985,16 @@ class DDRAce(
|
||||
3: self.GAME_RIVAL_SLOT_3_ACTIVE_OFFSET,
|
||||
}[rivalno]
|
||||
|
||||
whichfriend = lastdict.get_int(f'rival{rivalno}') - 1
|
||||
whichfriend = lastdict.get_int(f"rival{rivalno}") - 1
|
||||
if whichfriend < 0:
|
||||
# This rival isn't active
|
||||
rival[activeslot] = b'0'
|
||||
rival[activeslot] = b"0"
|
||||
continue
|
||||
|
||||
friendprofile = friends.get(whichfriend)
|
||||
if friendprofile is None:
|
||||
# This rival doesn't exist
|
||||
rival[activeslot] = b'0'
|
||||
rival[activeslot] = b"0"
|
||||
continue
|
||||
|
||||
ddrcodeslot = {
|
||||
@ -833,28 +1003,42 @@ class DDRAce(
|
||||
3: self.GAME_RIVAL_SLOT_3_DDRCODE_OFFSET,
|
||||
}[rivalno]
|
||||
|
||||
rival[activeslot] = acehex(rivalno).encode('ascii')
|
||||
rival[ddrcodeslot] = acehex(friendprofile.extid).encode('ascii')
|
||||
rival[activeslot] = acehex(rivalno).encode("ascii")
|
||||
rival[ddrcodeslot] = acehex(friendprofile.extid).encode(
|
||||
"ascii"
|
||||
)
|
||||
|
||||
usergamedata[ptype]['strdata'] = b','.join(rival)
|
||||
usergamedata[ptype]["strdata"] = b",".join(rival)
|
||||
|
||||
dnode = Node.string('d', base64.b64encode(usergamedata[ptype]['strdata']).decode('ascii'))
|
||||
dnode.add_child(Node.string('bin1', base64.b64encode(usergamedata[ptype]['bindata']).decode('ascii')))
|
||||
dnode = Node.string(
|
||||
"d",
|
||||
base64.b64encode(usergamedata[ptype]["strdata"]).decode(
|
||||
"ascii"
|
||||
),
|
||||
)
|
||||
dnode.add_child(
|
||||
Node.string(
|
||||
"bin1",
|
||||
base64.b64encode(usergamedata[ptype]["bindata"]).decode(
|
||||
"ascii"
|
||||
),
|
||||
)
|
||||
)
|
||||
record.add_child(dnode)
|
||||
|
||||
player.add_child(Node.u32('record_num', records))
|
||||
player.add_child(Node.u32("record_num", records))
|
||||
|
||||
playerdata.add_child(Node.s32('result', 0))
|
||||
playerdata.add_child(Node.s32("result", 0))
|
||||
return playerdata
|
||||
|
||||
def handle_system_convcardnumber_request(self, request: Node) -> Node:
|
||||
cardid = request.child_value('data/card_id')
|
||||
cardid = request.child_value("data/card_id")
|
||||
cardnumber = CardCipher.encode(cardid)
|
||||
|
||||
system = Node.void('system')
|
||||
data = Node.void('data')
|
||||
system = Node.void("system")
|
||||
data = Node.void("data")
|
||||
system.add_child(data)
|
||||
|
||||
system.add_child(Node.s32('result', 0))
|
||||
data.add_child(Node.string('card_number', cardnumber))
|
||||
system.add_child(Node.s32("result", 0))
|
||||
data.add_child(Node.string("card_number", cardnumber))
|
||||
return system
|
||||
|
@ -43,7 +43,7 @@ class DDRX2(
|
||||
DDRBase,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution X2'
|
||||
name: str = "DanceDanceRevolution X2"
|
||||
version: int = VersionConstants.DDR_X2
|
||||
|
||||
GAME_STYLE_SINGLE: Final[int] = 0
|
||||
@ -147,22 +147,24 @@ class DDRX2(
|
||||
return combo_type
|
||||
|
||||
def handle_game_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for flagid in range(256):
|
||||
flag = Node.void('flag')
|
||||
flag = Node.void("flag")
|
||||
game.add_child(flag)
|
||||
|
||||
flag.set_attribute('id', str(flagid))
|
||||
flag.set_attribute('s2', '0')
|
||||
flag.set_attribute('s1', '0')
|
||||
flag.set_attribute('t', '0')
|
||||
flag.set_attribute("id", str(flagid))
|
||||
flag.set_attribute("s2", "0")
|
||||
flag.set_attribute("s1", "0")
|
||||
flag.set_attribute("t", "0")
|
||||
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music", counts_by_reflink))
|
||||
|
||||
return game
|
||||
|
||||
@ -179,13 +181,16 @@ class DDRX2(
|
||||
sortedrecords[score.id] = {}
|
||||
sortedrecords[score.id][score.chart] = (userid, score)
|
||||
missing_profiles.append(userid)
|
||||
users = {userid: profile for (userid, profile) in self.get_any_profiles(missing_profiles)}
|
||||
users = {
|
||||
userid: profile
|
||||
for (userid, profile) in self.get_any_profiles(missing_profiles)
|
||||
}
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedrecords:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink_num', str(song))
|
||||
music.set_attribute("reclink_num", str(song))
|
||||
|
||||
for chart in sortedrecords[song]:
|
||||
userid, score = sortedrecords[song][chart]
|
||||
@ -194,24 +199,28 @@ class DDRX2(
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.string('name', users[userid].get_str('name')))
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('area', users[userid].get_int('area', self.get_machine_region())))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.string("name", users[userid].get_str("name")))
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(
|
||||
Node.u16(
|
||||
"area", users[userid].get_int("area", self.get_machine_region())
|
||||
)
|
||||
)
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if extid is not None:
|
||||
# Rival score loading
|
||||
@ -221,7 +230,9 @@ class DDRX2(
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
else:
|
||||
scores = []
|
||||
|
||||
@ -231,11 +242,11 @@ class DDRX2(
|
||||
sortedscores[score.id] = {}
|
||||
sortedscores[score.id][score.chart] = score
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedscores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink', str(song))
|
||||
music.set_attribute("reclink", str(song))
|
||||
|
||||
for chart in sortedscores[song]:
|
||||
score = sortedscores[song][chart]
|
||||
@ -244,39 +255,39 @@ class DDRX2(
|
||||
except KeyError:
|
||||
# Don't support this chart in this game
|
||||
continue
|
||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
||||
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.u32('score', score.points))
|
||||
typenode.add_child(Node.u16('count', score.plays))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32("score", score.points))
|
||||
typenode.add_child(Node.u16("count", score.plays))
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
songid = int(request.attribute('mid'))
|
||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
||||
refid = request.attribute("refid")
|
||||
songid = int(request.attribute("mid"))
|
||||
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||
|
||||
# Calculate statistics
|
||||
data = request.child('data')
|
||||
points = int(data.attribute('score'))
|
||||
combo = int(data.attribute('combo'))
|
||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
||||
if int(data.attribute('full')) == 0:
|
||||
data = request.child("data")
|
||||
points = int(data.attribute("score"))
|
||||
combo = int(data.attribute("combo"))
|
||||
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||
if int(data.attribute("full")) == 0:
|
||||
halo = self.HALO_NONE
|
||||
elif int(data.attribute('perf')) == 0:
|
||||
elif int(data.attribute("perf")) == 0:
|
||||
halo = self.HALO_GREAT_FULL_COMBO
|
||||
elif points < 1000000:
|
||||
halo = self.HALO_PERFECT_FULL_COMBO
|
||||
else:
|
||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
||||
trace = request.child_value('trace')
|
||||
trace = request.child_value("trace")
|
||||
|
||||
# Save the score, regardless of whether we have a refid. If we save
|
||||
# an anonymous score, it only goes into the DB to count against the
|
||||
@ -294,336 +305,372 @@ class DDRX2(
|
||||
)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('game')
|
||||
root = Node.void("game")
|
||||
|
||||
# Look up play stats we bridge to every mix
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Basic game settings
|
||||
root.add_child(Node.string('seq', ''))
|
||||
root.add_child(Node.u32('code', profile.extid))
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root.add_child(Node.u8('area', profile.get_int('area', self.get_machine_region())))
|
||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
||||
root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays')))
|
||||
root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
||||
root.add_child(Node.string("seq", ""))
|
||||
root.add_child(Node.u32("code", profile.extid))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
root.add_child(
|
||||
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
root.add_child(Node.u32("cnt_s", play_stats.get_int("single_plays")))
|
||||
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||
root.add_child(
|
||||
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||
) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
root.add_child(Node.u32("exp_o", profile.get_int("exp_o")))
|
||||
root.add_child(Node.u32("star", profile.get_int("star")))
|
||||
root.add_child(Node.u32("star_c", profile.get_int("star_c")))
|
||||
root.add_child(Node.u8("combo", profile.get_int("combo", 0)))
|
||||
root.add_child(Node.u8("timing_diff", profile.get_int("early_late", 0)))
|
||||
|
||||
# Character stuff
|
||||
chara = Node.void('chara')
|
||||
chara = Node.void("chara")
|
||||
root.add_child(chara)
|
||||
if 'chara' in profile:
|
||||
chara.set_attribute('my', str(profile.get_int('chara')))
|
||||
if "chara" in profile:
|
||||
chara.set_attribute("my", str(profile.get_int("chara")))
|
||||
|
||||
root.add_child(Node.u8_array('chara_opt', profile.get_int_array('chara_opt', 96)))
|
||||
root.add_child(
|
||||
Node.u8_array("chara_opt", profile.get_int_array("chara_opt", 96))
|
||||
)
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in profile:
|
||||
title = Node.void('title')
|
||||
if "title" in profile:
|
||||
title = Node.void("title")
|
||||
root.add_child(title)
|
||||
titledict = profile.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = profile.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in profile:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in profile:
|
||||
title_gr = Node.void("title_gr")
|
||||
root.add_child(title_gr)
|
||||
title_grdict = profile.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = profile.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Event progrses
|
||||
if 'event' in profile:
|
||||
event = Node.void('event')
|
||||
if "event" in profile:
|
||||
event = Node.void("event")
|
||||
root.add_child(event)
|
||||
event_dict = profile.get_dict('event')
|
||||
if 'diff_sum' in event_dict:
|
||||
event.set_attribute('diff_sum', str(event_dict.get_int('diff_sum')))
|
||||
if 'welcome' in event_dict:
|
||||
event.set_attribute('welcome', str(event_dict.get_int('welcome')))
|
||||
if 'e_flags' in event_dict:
|
||||
event.set_attribute('e_flags', str(event_dict.get_int('e_flags')))
|
||||
event_dict = profile.get_dict("event")
|
||||
if "diff_sum" in event_dict:
|
||||
event.set_attribute("diff_sum", str(event_dict.get_int("diff_sum")))
|
||||
if "welcome" in event_dict:
|
||||
event.set_attribute("welcome", str(event_dict.get_int("welcome")))
|
||||
if "e_flags" in event_dict:
|
||||
event.set_attribute("e_flags", str(event_dict.get_int("e_flags")))
|
||||
|
||||
if 'e_panel' in profile:
|
||||
e_panel = Node.void('e_panel')
|
||||
if "e_panel" in profile:
|
||||
e_panel = Node.void("e_panel")
|
||||
root.add_child(e_panel)
|
||||
e_panel_dict = profile.get_dict('e_panel')
|
||||
if 'play_id' in e_panel_dict:
|
||||
e_panel.set_attribute('play_id', str(e_panel_dict.get_int('play_id')))
|
||||
e_panel.add_child(Node.u8_array('cell', e_panel_dict.get_int_array('cell', 24)))
|
||||
e_panel.add_child(Node.u8_array('panel_state', e_panel_dict.get_int_array('panel_state', 6)))
|
||||
e_panel_dict = profile.get_dict("e_panel")
|
||||
if "play_id" in e_panel_dict:
|
||||
e_panel.set_attribute("play_id", str(e_panel_dict.get_int("play_id")))
|
||||
e_panel.add_child(
|
||||
Node.u8_array("cell", e_panel_dict.get_int_array("cell", 24))
|
||||
)
|
||||
e_panel.add_child(
|
||||
Node.u8_array(
|
||||
"panel_state", e_panel_dict.get_int_array("panel_state", 6)
|
||||
)
|
||||
)
|
||||
|
||||
if 'e_pix' in profile:
|
||||
e_pix = Node.void('e_pix')
|
||||
if "e_pix" in profile:
|
||||
e_pix = Node.void("e_pix")
|
||||
root.add_child(e_pix)
|
||||
e_pix_dict = profile.get_dict('e_pix')
|
||||
if 'max_distance' in e_pix_dict:
|
||||
e_pix.set_attribute('max_distance', str(e_pix_dict.get_int('max_distance')))
|
||||
if 'max_planet' in e_pix_dict:
|
||||
e_pix.set_attribute('max_planet', str(e_pix_dict.get_int('max_planet')))
|
||||
if 'total_distance' in e_pix_dict:
|
||||
e_pix.set_attribute('total_distance', str(e_pix_dict.get_int('total_distance')))
|
||||
if 'total_planet' in e_pix_dict:
|
||||
e_pix.set_attribute('total_planet', str(e_pix_dict.get_int('total_planet')))
|
||||
if 'border_character' in e_pix_dict:
|
||||
e_pix.set_attribute('border_character', str(e_pix_dict.get_int('border_character')))
|
||||
if 'border_balloon' in e_pix_dict:
|
||||
e_pix.set_attribute('border_balloon', str(e_pix_dict.get_int('border_balloon')))
|
||||
if 'border_music_aftr' in e_pix_dict:
|
||||
e_pix.set_attribute('border_music_aftr', str(e_pix_dict.get_int('border_music_aftr')))
|
||||
if 'border_music_meii' in e_pix_dict:
|
||||
e_pix.set_attribute('border_music_meii', str(e_pix_dict.get_int('border_music_meii')))
|
||||
if 'border_music_dirt' in e_pix_dict:
|
||||
e_pix.set_attribute('border_music_dirt', str(e_pix_dict.get_int('border_music_dirt')))
|
||||
if 'flags' in e_pix_dict:
|
||||
e_pix.set_attribute('flags', str(e_pix_dict.get_int('flags')))
|
||||
e_pix_dict = profile.get_dict("e_pix")
|
||||
if "max_distance" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"max_distance", str(e_pix_dict.get_int("max_distance"))
|
||||
)
|
||||
if "max_planet" in e_pix_dict:
|
||||
e_pix.set_attribute("max_planet", str(e_pix_dict.get_int("max_planet")))
|
||||
if "total_distance" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"total_distance", str(e_pix_dict.get_int("total_distance"))
|
||||
)
|
||||
if "total_planet" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"total_planet", str(e_pix_dict.get_int("total_planet"))
|
||||
)
|
||||
if "border_character" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"border_character", str(e_pix_dict.get_int("border_character"))
|
||||
)
|
||||
if "border_balloon" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"border_balloon", str(e_pix_dict.get_int("border_balloon"))
|
||||
)
|
||||
if "border_music_aftr" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"border_music_aftr", str(e_pix_dict.get_int("border_music_aftr"))
|
||||
)
|
||||
if "border_music_meii" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"border_music_meii", str(e_pix_dict.get_int("border_music_meii"))
|
||||
)
|
||||
if "border_music_dirt" in e_pix_dict:
|
||||
e_pix.set_attribute(
|
||||
"border_music_dirt", str(e_pix_dict.get_int("border_music_dirt"))
|
||||
)
|
||||
if "flags" in e_pix_dict:
|
||||
e_pix.set_attribute("flags", str(e_pix_dict.get_int("flags")))
|
||||
|
||||
# Calorie mode
|
||||
if 'weight' in profile:
|
||||
if "weight" in profile:
|
||||
workouts = self.data.local.user.get_time_based_achievements(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
achievementtype='workout',
|
||||
achievementtype="workout",
|
||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
||||
)
|
||||
total = sum([w.data.get_int('calories') for w in workouts])
|
||||
workout = Node.void('workout')
|
||||
total = sum([w.data.get_int("calories") for w in workouts])
|
||||
workout = Node.void("workout")
|
||||
root.add_child(workout)
|
||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
||||
workout.set_attribute('day', str(total))
|
||||
workout.set_attribute('disp', '1')
|
||||
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||
workout.set_attribute("day", str(total))
|
||||
workout.set_attribute("disp", "1")
|
||||
|
||||
# Last cursor settings
|
||||
last = Node.void('last')
|
||||
last = Node.void("last")
|
||||
root.add_child(last)
|
||||
lastdict = profile.get_dict('last')
|
||||
last.set_attribute('fri', str(lastdict.get_int('fri')))
|
||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
||||
lastdict = profile.get_dict("last")
|
||||
last.set_attribute("fri", str(lastdict.get_int("fri")))
|
||||
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||
last.set_attribute("cid", str(lastdict.get_int("cid")))
|
||||
last.set_attribute("ctype", str(lastdict.get_int("ctype")))
|
||||
last.set_attribute("sid", str(lastdict.get_int("sid")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
root.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
root.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Options in menus
|
||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
|
||||
root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))
|
||||
root.add_child(Node.s16_array("opt", profile.get_int_array("opt", 16)))
|
||||
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||
|
||||
# Unlock flags
|
||||
root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 256, [1] * 256)))
|
||||
root.add_child(
|
||||
Node.u8_array("flag", profile.get_int_array("flag", 256, [1] * 256))
|
||||
)
|
||||
|
||||
# Ranking display?
|
||||
root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))
|
||||
root.add_child(Node.u16_array("rank", profile.get_int_array("rank", 100)))
|
||||
|
||||
# Rivals
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
friend = self.get_profile(link.other_userid)
|
||||
play_stats = self.get_play_statistics(link.other_userid)
|
||||
if friend is not None:
|
||||
friendnode = Node.void('friend')
|
||||
friendnode = Node.void("friend")
|
||||
root.add_child(friendnode)
|
||||
friendnode.set_attribute('pos', str(pos))
|
||||
friendnode.set_attribute('vs', '0')
|
||||
friendnode.set_attribute('up', '0')
|
||||
friendnode.add_child(Node.u32('code', friend.extid))
|
||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
||||
friendnode.add_child(Node.u8('area', friend.get_int('area', self.get_machine_region())))
|
||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
||||
friendnode.set_attribute("pos", str(pos))
|
||||
friendnode.set_attribute("vs", "0")
|
||||
friendnode.set_attribute("up", "0")
|
||||
friendnode.add_child(Node.u32("code", friend.extid))
|
||||
friendnode.add_child(Node.string("name", friend.get_str("name")))
|
||||
friendnode.add_child(
|
||||
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in friend:
|
||||
title = Node.void('title')
|
||||
if "title" in friend:
|
||||
title = Node.void("title")
|
||||
friendnode.add_child(title)
|
||||
titledict = friend.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = friend.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in friend:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in friend:
|
||||
title_gr = Node.void("title_gr")
|
||||
friendnode.add_child(title_gr)
|
||||
title_grdict = friend.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = friend.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
friendnode.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
friendnode.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Grab last node and accessories so we can make decisions based on type
|
||||
last = request.child('last')
|
||||
lastdict = newprofile.get_dict('last')
|
||||
mode = int(last.attribute('mode'))
|
||||
style = int(last.attribute('style'))
|
||||
last = request.child("last")
|
||||
lastdict = newprofile.get_dict("last")
|
||||
mode = int(last.attribute("mode"))
|
||||
style = int(last.attribute("style"))
|
||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||
|
||||
# Drill rankings
|
||||
title = request.child('title')
|
||||
title_gr = request.child('title_gr')
|
||||
titledict = newprofile.get_dict('title')
|
||||
title_grdict = newprofile.get_dict('title_gr')
|
||||
title = request.child("title")
|
||||
title_gr = request.child("title_gr")
|
||||
titledict = newprofile.get_dict("title")
|
||||
title_grdict = newprofile.get_dict("title_gr")
|
||||
|
||||
# Groove radar level ups
|
||||
gr = request.child('gr')
|
||||
gr = request.child("gr")
|
||||
|
||||
# Set the correct values depending on if we're single or double play
|
||||
if is_dp:
|
||||
play_stats.increment_int('double_plays')
|
||||
play_stats.increment_int("double_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_d',
|
||||
"gr_d",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
title_grdict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
else:
|
||||
play_stats.increment_int('single_plays')
|
||||
play_stats.increment_int("single_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_s',
|
||||
"gr_s",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
play_stats.increment_int(f'cnt_m{mode}')
|
||||
title_grdict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
play_stats.increment_int(f"cnt_m{mode}")
|
||||
|
||||
# Update last attributes
|
||||
lastdict.replace_int('fri', intish(last.attribute('fri')))
|
||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
lastdict.replace_int("fri", intish(last.attribute("fri")))
|
||||
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Grab character options
|
||||
chara = request.child('chara')
|
||||
chara = request.child("chara")
|
||||
if chara is not None:
|
||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
||||
newprofile.replace_int_array('chara_opt', 96, request.child_value('chara_opt'))
|
||||
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||
newprofile.replace_int_array("chara_opt", 96, request.child_value("chara_opt"))
|
||||
|
||||
# Track event progress
|
||||
event = request.child('event')
|
||||
event = request.child("event")
|
||||
if event is not None:
|
||||
event_dict = newprofile.get_dict('event')
|
||||
event_dict.replace_int('diff_sum', intish(event.attribute('diff_sum')))
|
||||
event_dict.replace_int('e_flags', intish(event.attribute('e_flags')))
|
||||
event_dict.replace_int('welcome', intish(event.attribute('welcome')))
|
||||
newprofile.replace_dict('event', event_dict)
|
||||
event_dict = newprofile.get_dict("event")
|
||||
event_dict.replace_int("diff_sum", intish(event.attribute("diff_sum")))
|
||||
event_dict.replace_int("e_flags", intish(event.attribute("e_flags")))
|
||||
event_dict.replace_int("welcome", intish(event.attribute("welcome")))
|
||||
newprofile.replace_dict("event", event_dict)
|
||||
|
||||
e_panel = request.child('e_panel')
|
||||
e_panel = request.child("e_panel")
|
||||
if e_panel is not None:
|
||||
e_panel_dict = newprofile.get_dict('e_panel')
|
||||
e_panel_dict.replace_int('play_id', intish(e_panel.attribute('play_id')))
|
||||
e_panel_dict.replace_int_array('cell', 24, e_panel.child_value('cell'))
|
||||
e_panel_dict.replace_int_array('panel_state', 6, e_panel.child_value('panel_state'))
|
||||
newprofile.replace_dict('e_panel', e_panel_dict)
|
||||
e_panel_dict = newprofile.get_dict("e_panel")
|
||||
e_panel_dict.replace_int("play_id", intish(e_panel.attribute("play_id")))
|
||||
e_panel_dict.replace_int_array("cell", 24, e_panel.child_value("cell"))
|
||||
e_panel_dict.replace_int_array(
|
||||
"panel_state", 6, e_panel.child_value("panel_state")
|
||||
)
|
||||
newprofile.replace_dict("e_panel", e_panel_dict)
|
||||
|
||||
e_pix = request.child('e_pix')
|
||||
e_pix = request.child("e_pix")
|
||||
if e_pix is not None:
|
||||
e_pix_dict = newprofile.get_dict('e_pix')
|
||||
max_distance = e_pix_dict.get_int('max_distance')
|
||||
max_planet = e_pix_dict.get_int('max_planet')
|
||||
total_distance = e_pix_dict.get_int('total_distance')
|
||||
total_planet = e_pix_dict.get_int('total_planet')
|
||||
cur_distance = intish(e_pix.attribute('this_distance'))
|
||||
cur_planet = intish(e_pix.attribute('this_planet'))
|
||||
e_pix_dict = newprofile.get_dict("e_pix")
|
||||
max_distance = e_pix_dict.get_int("max_distance")
|
||||
max_planet = e_pix_dict.get_int("max_planet")
|
||||
total_distance = e_pix_dict.get_int("total_distance")
|
||||
total_planet = e_pix_dict.get_int("total_planet")
|
||||
cur_distance = intish(e_pix.attribute("this_distance"))
|
||||
cur_planet = intish(e_pix.attribute("this_planet"))
|
||||
if cur_distance is not None:
|
||||
max_distance = max(max_distance, cur_distance)
|
||||
total_distance += cur_distance
|
||||
@ -631,51 +678,51 @@ class DDRX2(
|
||||
max_planet = max(max_planet, cur_planet)
|
||||
total_planet += cur_planet
|
||||
|
||||
e_pix_dict.replace_int('max_distance', max_distance)
|
||||
e_pix_dict.replace_int('max_planet', max_planet)
|
||||
e_pix_dict.replace_int('total_distance', total_distance)
|
||||
e_pix_dict.replace_int('total_planet', total_planet)
|
||||
e_pix_dict.replace_int('flags', intish(e_pix.attribute('flags')))
|
||||
newprofile.replace_dict('e_pix', e_pix_dict)
|
||||
e_pix_dict.replace_int("max_distance", max_distance)
|
||||
e_pix_dict.replace_int("max_planet", max_planet)
|
||||
e_pix_dict.replace_int("total_distance", total_distance)
|
||||
e_pix_dict.replace_int("total_planet", total_planet)
|
||||
e_pix_dict.replace_int("flags", intish(e_pix.attribute("flags")))
|
||||
newprofile.replace_dict("e_pix", e_pix_dict)
|
||||
|
||||
# Options
|
||||
opt = request.child('opt')
|
||||
opt = request.child("opt")
|
||||
if opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('opt', 16, opt.value[:16])
|
||||
newprofile.replace_int_array("opt", 16, opt.value[:16])
|
||||
|
||||
# Experience and stars
|
||||
exp = request.child_value('exp')
|
||||
exp = request.child_value("exp")
|
||||
if exp is not None:
|
||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
||||
star = request.child_value('star')
|
||||
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||
star = request.child_value("star")
|
||||
if star is not None:
|
||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
||||
star_c = request.child_value('star_c')
|
||||
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||
star_c = request.child_value("star_c")
|
||||
if star_c is not None:
|
||||
newprofile.replace_int('star_c', newprofile.get_int('star_c') + exp)
|
||||
newprofile.replace_int("star_c", newprofile.get_int("star_c") + exp)
|
||||
|
||||
# Update game flags
|
||||
for child in request.children:
|
||||
if child.name != 'flag':
|
||||
if child.name != "flag":
|
||||
continue
|
||||
try:
|
||||
value = int(child.attribute('data'))
|
||||
offset = int(child.attribute('no'))
|
||||
value = int(child.attribute("data"))
|
||||
offset = int(child.attribute("no"))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
flags = newprofile.get_int_array('flag', 256, [1] * 256)
|
||||
flags = newprofile.get_int_array("flag", 256, [1] * 256)
|
||||
if offset < 0 or offset >= len(flags):
|
||||
continue
|
||||
flags[offset] = value
|
||||
newprofile.replace_int_array('flag', 256, flags)
|
||||
newprofile.replace_int_array("flag", 256, flags)
|
||||
|
||||
# Workout mode support
|
||||
newweight = -1
|
||||
oldweight = newprofile.get_int('weight')
|
||||
oldweight = newprofile.get_int("weight")
|
||||
for child in request.children:
|
||||
if child.name != 'weight':
|
||||
if child.name != "weight":
|
||||
continue
|
||||
newweight = child.value
|
||||
if newweight < 0:
|
||||
@ -684,14 +731,14 @@ class DDRX2(
|
||||
# Either update or unset the weight depending on the game
|
||||
if newweight == 0:
|
||||
# Weight is unset or we declined to use this feature, remove from profile
|
||||
if 'weight' in newprofile:
|
||||
del newprofile['weight']
|
||||
if "weight" in newprofile:
|
||||
del newprofile["weight"]
|
||||
else:
|
||||
# Weight has been set or previously retrieved, we should save calories
|
||||
newprofile.replace_int('weight', newweight)
|
||||
newprofile.replace_int("weight", newweight)
|
||||
total = 0
|
||||
for child in request.children:
|
||||
if child.name != 'calory':
|
||||
if child.name != "calory":
|
||||
continue
|
||||
total += child.value
|
||||
self.data.local.user.put_time_based_achievement(
|
||||
@ -699,10 +746,10 @@ class DDRX2(
|
||||
self.version,
|
||||
userid,
|
||||
0,
|
||||
'workout',
|
||||
"workout",
|
||||
{
|
||||
'calories': total,
|
||||
'weight': newweight,
|
||||
"calories": total,
|
||||
"weight": newweight,
|
||||
},
|
||||
)
|
||||
|
||||
@ -710,7 +757,7 @@ class DDRX2(
|
||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
@ -719,11 +766,11 @@ class DDRX2(
|
||||
# Save any rivals that were added/removed/changed
|
||||
newfriends = oldfriends[:]
|
||||
for child in request.children:
|
||||
if child.name != 'friend':
|
||||
if child.name != "friend":
|
||||
continue
|
||||
|
||||
code = int(child.attribute('code'))
|
||||
pos = int(child.attribute('pos'))
|
||||
code = int(child.attribute("code"))
|
||||
pos = int(child.attribute("pos"))
|
||||
|
||||
if pos >= 0 and pos < 10:
|
||||
if code == 0:
|
||||
@ -731,7 +778,9 @@ class DDRX2(
|
||||
newfriends[pos] = None
|
||||
else:
|
||||
# Try looking up the userid
|
||||
newfriends[pos] = self.data.remote.user.from_extid(self.game, self.version, code)
|
||||
newfriends[pos] = self.data.remote.user.from_extid(
|
||||
self.game, self.version, code
|
||||
)
|
||||
|
||||
# Diff the set of links to determine updates
|
||||
for i in range(10):
|
||||
@ -744,7 +793,7 @@ class DDRX2(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
elif oldfriends[i] is None:
|
||||
@ -753,7 +802,7 @@ class DDRX2(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
@ -763,14 +812,14 @@ class DDRX2(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
self.data.local.user.put_link(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
|
@ -49,7 +49,7 @@ class DDRX3(
|
||||
DDRBase,
|
||||
):
|
||||
|
||||
name: str = 'DanceDanceRevolution X3 VS 2ndMIX'
|
||||
name: str = "DanceDanceRevolution X3 VS 2ndMIX"
|
||||
version: int = VersionConstants.DDR_X3_VS_2NDMIX
|
||||
|
||||
GAME_STYLE_SINGLE: Final[int] = 0
|
||||
@ -156,28 +156,30 @@ class DDRX3(
|
||||
return combo_type
|
||||
|
||||
def handle_game_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for flagid in range(256):
|
||||
flag = Node.void('flag')
|
||||
flag = Node.void("flag")
|
||||
game.add_child(flag)
|
||||
|
||||
flag.set_attribute('id', str(flagid))
|
||||
flag.set_attribute('s2', '0')
|
||||
flag.set_attribute('s1', '0')
|
||||
flag.set_attribute('t', '0')
|
||||
flag.set_attribute("id", str(flagid))
|
||||
flag.set_attribute("s2", "0")
|
||||
flag.set_attribute("s1", "0")
|
||||
flag.set_attribute("t", "0")
|
||||
|
||||
hit_chart = self.data.local.music.get_hit_chart(self.game, self.music_version, self.GAME_MAX_SONGS)
|
||||
hit_chart = self.data.local.music.get_hit_chart(
|
||||
self.game, self.music_version, self.GAME_MAX_SONGS
|
||||
)
|
||||
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||
for (reflink, plays) in hit_chart:
|
||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||
counts_by_reflink[reflink] = plays
|
||||
game.add_child(Node.u32_array('cnt_music', counts_by_reflink))
|
||||
game.add_child(Node.u32_array("cnt_music", counts_by_reflink))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||
extid = intish(request.attribute('code'))
|
||||
refid = request.attribute('refid')
|
||||
extid = intish(request.attribute("code"))
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if extid is not None:
|
||||
# Rival score loading
|
||||
@ -187,10 +189,15 @@ class DDRX3(
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
old_scores = [
|
||||
score for score in self.data.local.user.get_achievements(self.game, self.music_version, userid)
|
||||
if score.type == '2ndmix'
|
||||
score
|
||||
for score in self.data.local.user.get_achievements(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
if score.type == "2ndmix"
|
||||
]
|
||||
else:
|
||||
scores = []
|
||||
@ -203,7 +210,7 @@ class DDRX3(
|
||||
sortedscores[score.id] = {}
|
||||
if score.chart not in sortedscores[score.id]:
|
||||
sortedscores[score.id][score.chart] = {}
|
||||
sortedscores[score.id][score.chart]['score'] = score
|
||||
sortedscores[score.id][score.chart]["score"] = score
|
||||
|
||||
for oldscore in old_scores:
|
||||
songid = int(oldscore.id / 100)
|
||||
@ -212,13 +219,13 @@ class DDRX3(
|
||||
sortedscores[songid] = {}
|
||||
if chart not in sortedscores[songid]:
|
||||
sortedscores[songid][chart] = {}
|
||||
sortedscores[songid][chart]['oldscore'] = oldscore
|
||||
sortedscores[songid][chart]["oldscore"] = oldscore
|
||||
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for song in sortedscores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('reclink', str(song))
|
||||
music.set_attribute("reclink", str(song))
|
||||
|
||||
for chart in sortedscores[song]:
|
||||
try:
|
||||
@ -228,12 +235,16 @@ class DDRX3(
|
||||
continue
|
||||
scoredict = sortedscores[song][chart]
|
||||
|
||||
if 'score' in scoredict:
|
||||
if "score" in scoredict:
|
||||
# We played the normal version of this song
|
||||
gamerank = self.db_to_game_rank(scoredict['score'].data.get_int('rank'))
|
||||
combo_type = self.db_to_game_halo(scoredict['score'].data.get_int('halo'))
|
||||
points = scoredict['score'].points # type: ignore
|
||||
plays = scoredict['score'].plays # type: ignore
|
||||
gamerank = self.db_to_game_rank(
|
||||
scoredict["score"].data.get_int("rank")
|
||||
)
|
||||
combo_type = self.db_to_game_halo(
|
||||
scoredict["score"].data.get_int("halo")
|
||||
)
|
||||
points = scoredict["score"].points # type: ignore
|
||||
plays = scoredict["score"].plays # type: ignore
|
||||
else:
|
||||
# We only played 2nd mix version of this song
|
||||
gamerank = 0
|
||||
@ -241,45 +252,45 @@ class DDRX3(
|
||||
points = 0
|
||||
plays = 0
|
||||
|
||||
if 'oldscore' in scoredict:
|
||||
if "oldscore" in scoredict:
|
||||
# We played the 2nd mix version of this song
|
||||
oldpoints = scoredict['oldscore'].data.get_int('points')
|
||||
oldrank = scoredict['oldscore'].data.get_int('rank')
|
||||
oldplays = scoredict['oldscore'].data.get_int('plays')
|
||||
oldpoints = scoredict["oldscore"].data.get_int("points")
|
||||
oldrank = scoredict["oldscore"].data.get_int("rank")
|
||||
oldplays = scoredict["oldscore"].data.get_int("plays")
|
||||
else:
|
||||
oldpoints = 0
|
||||
oldrank = 0
|
||||
oldplays = 0
|
||||
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('diff', str(gamechart))
|
||||
typenode.set_attribute("diff", str(gamechart))
|
||||
|
||||
typenode.add_child(Node.u32('score', points))
|
||||
typenode.add_child(Node.u16('count', plays))
|
||||
typenode.add_child(Node.u8('rank', gamerank))
|
||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
||||
typenode.add_child(Node.u32('score_2nd', oldpoints))
|
||||
typenode.add_child(Node.u8('rank_2nd', oldrank))
|
||||
typenode.add_child(Node.u16('cnt_2nd', oldplays))
|
||||
typenode.add_child(Node.u32("score", points))
|
||||
typenode.add_child(Node.u16("count", plays))
|
||||
typenode.add_child(Node.u8("rank", gamerank))
|
||||
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||
typenode.add_child(Node.u32("score_2nd", oldpoints))
|
||||
typenode.add_child(Node.u8("rank_2nd", oldrank))
|
||||
typenode.add_child(Node.u16("cnt_2nd", oldplays))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
songid = int(request.attribute('mid'))
|
||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
||||
refid = request.attribute("refid")
|
||||
songid = int(request.attribute("mid"))
|
||||
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
|
||||
# Calculate statistics
|
||||
data = request.child('data')
|
||||
playmode = int(data.attribute('playmode'))
|
||||
data = request.child("data")
|
||||
playmode = int(data.attribute("playmode"))
|
||||
if playmode == self.GAME_PLAY_MODE_2NDMIX:
|
||||
if userid is not None:
|
||||
# These are special cased and treated like achievements
|
||||
points = int(data.attribute('score_2nd'))
|
||||
combo = int(data.attribute('combo_2nd'))
|
||||
rank = int(data.attribute('rank_2nd'))
|
||||
points = int(data.attribute("score_2nd"))
|
||||
combo = int(data.attribute("combo_2nd"))
|
||||
rank = int(data.attribute("rank_2nd"))
|
||||
|
||||
# Grab the old 2nd mix score
|
||||
existingscore = self.data.local.user.get_achievement(
|
||||
@ -287,17 +298,17 @@ class DDRX3(
|
||||
self.music_version,
|
||||
userid,
|
||||
(songid * 100) + chart,
|
||||
'2ndmix',
|
||||
"2ndmix",
|
||||
)
|
||||
|
||||
if existingscore is not None:
|
||||
highscore = points > existingscore.get_int('points')
|
||||
highscore = points > existingscore.get_int("points")
|
||||
|
||||
plays = existingscore.get_int('plays', 0) + 1
|
||||
points = max(points, existingscore.get_int('points'))
|
||||
plays = existingscore.get_int("plays", 0) + 1
|
||||
points = max(points, existingscore.get_int("points"))
|
||||
if not highscore:
|
||||
combo = existingscore.get_int('combo', combo)
|
||||
rank = existingscore.get_int('rank', rank)
|
||||
combo = existingscore.get_int("combo", combo)
|
||||
rank = existingscore.get_int("rank", rank)
|
||||
else:
|
||||
plays = 1
|
||||
|
||||
@ -306,27 +317,27 @@ class DDRX3(
|
||||
self.music_version,
|
||||
userid,
|
||||
(songid * 100) + chart,
|
||||
'2ndmix',
|
||||
"2ndmix",
|
||||
{
|
||||
'points': points,
|
||||
'combo': combo,
|
||||
'rank': rank,
|
||||
'plays': plays,
|
||||
"points": points,
|
||||
"combo": combo,
|
||||
"rank": rank,
|
||||
"plays": plays,
|
||||
},
|
||||
)
|
||||
else:
|
||||
points = int(data.attribute('score'))
|
||||
combo = int(data.attribute('combo'))
|
||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
||||
if int(data.attribute('full')) == 0:
|
||||
points = int(data.attribute("score"))
|
||||
combo = int(data.attribute("combo"))
|
||||
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||
if int(data.attribute("full")) == 0:
|
||||
halo = self.HALO_NONE
|
||||
elif int(data.attribute('perf')) == 0:
|
||||
elif int(data.attribute("perf")) == 0:
|
||||
halo = self.HALO_GREAT_FULL_COMBO
|
||||
elif points < 1000000:
|
||||
halo = self.HALO_PERFECT_FULL_COMBO
|
||||
else:
|
||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
||||
trace = request.child_value('trace')
|
||||
trace = request.child_value("trace")
|
||||
|
||||
# Save the score, regardless of whether we have a refid. If we save
|
||||
# an anonymous score, it only goes into the DB to count against the
|
||||
@ -343,355 +354,371 @@ class DDRX3(
|
||||
)
|
||||
|
||||
# No response needed
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('game')
|
||||
root = Node.void("game")
|
||||
|
||||
# Look up play stats we bridge to every mix
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Basic game settings
|
||||
root.add_child(Node.string('seq', ''))
|
||||
root.add_child(Node.u32('code', profile.extid))
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root.add_child(Node.u8('area', profile.get_int('area', self.get_machine_region())))
|
||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
||||
root.add_child(Node.u32('cnt_d', play_stats.get_int('double_plays')))
|
||||
root.add_child(Node.u32('cnt_b', play_stats.get_int('battle_plays'))) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
||||
root.add_child(Node.string("seq", ""))
|
||||
root.add_child(Node.u32("code", profile.extid))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
root.add_child(
|
||||
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
root.add_child(Node.u32("cnt_s", play_stats.get_int("single_plays")))
|
||||
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||
root.add_child(
|
||||
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||
) # This could be wrong, its a guess
|
||||
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
root.add_child(Node.u32("exp_o", profile.get_int("exp_o")))
|
||||
root.add_child(Node.u32("star", profile.get_int("star")))
|
||||
root.add_child(Node.u32("star_c", profile.get_int("star_c")))
|
||||
root.add_child(Node.u8("combo", profile.get_int("combo", 0)))
|
||||
root.add_child(Node.u8("timing_diff", profile.get_int("early_late", 0)))
|
||||
|
||||
# Character stuff
|
||||
chara = Node.void('chara')
|
||||
chara = Node.void("chara")
|
||||
root.add_child(chara)
|
||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
||||
root.add_child(Node.u16_array('chara_opt', profile.get_int_array('chara_opt', 96, [208] * 96)))
|
||||
chara.set_attribute("my", str(profile.get_int("chara", 30)))
|
||||
root.add_child(
|
||||
Node.u16_array(
|
||||
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||
)
|
||||
)
|
||||
|
||||
# Drill rankings
|
||||
if 'title_gr' in profile:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in profile:
|
||||
title_gr = Node.void("title_gr")
|
||||
root.add_child(title_gr)
|
||||
title_grdict = profile.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = profile.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Calorie mode
|
||||
if 'weight' in profile:
|
||||
if "weight" in profile:
|
||||
workouts = self.data.local.user.get_time_based_achievements(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
achievementtype='workout',
|
||||
achievementtype="workout",
|
||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
||||
)
|
||||
total = sum([w.data.get_int('calories') for w in workouts])
|
||||
workout = Node.void('workout')
|
||||
total = sum([w.data.get_int("calories") for w in workouts])
|
||||
workout = Node.void("workout")
|
||||
root.add_child(workout)
|
||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
||||
workout.set_attribute('day', str(total))
|
||||
workout.set_attribute('disp', '1')
|
||||
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||
workout.set_attribute("day", str(total))
|
||||
workout.set_attribute("disp", "1")
|
||||
|
||||
# Daily play counts
|
||||
daycount = Node.void('daycount')
|
||||
daycount = Node.void("daycount")
|
||||
root.add_child(daycount)
|
||||
daycount.set_attribute('playcount', str(play_stats.today_plays))
|
||||
daycount.set_attribute("playcount", str(play_stats.today_plays))
|
||||
|
||||
# Daily combo stuff, unknown how this works
|
||||
dailycombo = Node.void('dailycombo')
|
||||
dailycombo = Node.void("dailycombo")
|
||||
root.add_child(dailycombo)
|
||||
dailycombo.set_attribute('daily_combo', str(0))
|
||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
||||
dailycombo.set_attribute("daily_combo", str(0))
|
||||
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||
|
||||
# Last cursor settings
|
||||
last = Node.void('last')
|
||||
last = Node.void("last")
|
||||
root.add_child(last)
|
||||
lastdict = profile.get_dict('last')
|
||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -1)))
|
||||
last.set_attribute('fri', str(lastdict.get_int('rival1', -1))) # This literally goes to the same memory in X3
|
||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
||||
lastdict = profile.get_dict("last")
|
||||
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||
last.set_attribute("rival3", str(lastdict.get_int("rival3", -1)))
|
||||
last.set_attribute(
|
||||
"fri", str(lastdict.get_int("rival1", -1))
|
||||
) # This literally goes to the same memory in X3
|
||||
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||
last.set_attribute("cid", str(lastdict.get_int("cid")))
|
||||
last.set_attribute("ctype", str(lastdict.get_int("ctype")))
|
||||
last.set_attribute("sid", str(lastdict.get_int("sid")))
|
||||
|
||||
# Result stars
|
||||
result_star = Node.void('result_star')
|
||||
result_star = Node.void("result_star")
|
||||
root.add_child(result_star)
|
||||
result_stars = profile.get_int_array('result_stars', 9)
|
||||
result_stars = profile.get_int_array("result_stars", 9)
|
||||
for i in range(9):
|
||||
result_star.set_attribute(f'slot{i + 1}', str(result_stars[i]))
|
||||
result_star.set_attribute(f"slot{i + 1}", str(result_stars[i]))
|
||||
|
||||
# Target stuff
|
||||
target = Node.void('target')
|
||||
target = Node.void("target")
|
||||
root.add_child(target)
|
||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
||||
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
root.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
root.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in profile.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in profile.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Options in menus
|
||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 16)))
|
||||
root.add_child(Node.s16_array('opt_ex', profile.get_int_array('opt_ex', 16)))
|
||||
root.add_child(Node.s16_array("opt", profile.get_int_array("opt", 16)))
|
||||
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||
|
||||
# Unlock flags
|
||||
root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 256, [1] * 256)))
|
||||
root.add_child(
|
||||
Node.u8_array("flag", profile.get_int_array("flag", 256, [1] * 256))
|
||||
)
|
||||
|
||||
# Ranking display?
|
||||
root.add_child(Node.u16_array('rank', profile.get_int_array('rank', 100)))
|
||||
root.add_child(Node.u16_array("rank", profile.get_int_array("rank", 100)))
|
||||
|
||||
# Rivals
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
friend = self.get_profile(link.other_userid)
|
||||
play_stats = self.get_play_statistics(link.other_userid)
|
||||
if friend is not None:
|
||||
friendnode = Node.void('friend')
|
||||
friendnode = Node.void("friend")
|
||||
root.add_child(friendnode)
|
||||
friendnode.set_attribute('pos', str(pos))
|
||||
friendnode.set_attribute('vs', '0')
|
||||
friendnode.set_attribute('up', '0')
|
||||
friendnode.add_child(Node.u32('code', friend.extid))
|
||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
||||
friendnode.add_child(Node.u8('area', friend.get_int('area', self.get_machine_region())))
|
||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
||||
friendnode.set_attribute("pos", str(pos))
|
||||
friendnode.set_attribute("vs", "0")
|
||||
friendnode.set_attribute("up", "0")
|
||||
friendnode.add_child(Node.u32("code", friend.extid))
|
||||
friendnode.add_child(Node.string("name", friend.get_str("name")))
|
||||
friendnode.add_child(
|
||||
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||
)
|
||||
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||
|
||||
# Drill rankings
|
||||
if 'title' in friend:
|
||||
title = Node.void('title')
|
||||
if "title" in friend:
|
||||
title = Node.void("title")
|
||||
friendnode.add_child(title)
|
||||
titledict = friend.get_dict('title')
|
||||
if 't' in titledict:
|
||||
title.set_attribute('t', str(titledict.get_int('t')))
|
||||
if 's' in titledict:
|
||||
title.set_attribute('s', str(titledict.get_int('s')))
|
||||
if 'd' in titledict:
|
||||
title.set_attribute('d', str(titledict.get_int('d')))
|
||||
titledict = friend.get_dict("title")
|
||||
if "t" in titledict:
|
||||
title.set_attribute("t", str(titledict.get_int("t")))
|
||||
if "s" in titledict:
|
||||
title.set_attribute("s", str(titledict.get_int("s")))
|
||||
if "d" in titledict:
|
||||
title.set_attribute("d", str(titledict.get_int("d")))
|
||||
|
||||
if 'title_gr' in friend:
|
||||
title_gr = Node.void('title_gr')
|
||||
if "title_gr" in friend:
|
||||
title_gr = Node.void("title_gr")
|
||||
friendnode.add_child(title_gr)
|
||||
title_grdict = friend.get_dict('title_gr')
|
||||
if 't' in title_grdict:
|
||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
||||
if 's' in title_grdict:
|
||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
||||
if 'd' in title_grdict:
|
||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
||||
title_grdict = friend.get_dict("title_gr")
|
||||
if "t" in title_grdict:
|
||||
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||
if "s" in title_grdict:
|
||||
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||
if "d" in title_grdict:
|
||||
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||
|
||||
# Groove gauge level-ups
|
||||
gr_s = Node.void('gr_s')
|
||||
gr_s = Node.void("gr_s")
|
||||
friendnode.add_child(gr_s)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_s', 5):
|
||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_s", 5):
|
||||
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
gr_d = Node.void('gr_d')
|
||||
gr_d = Node.void("gr_d")
|
||||
friendnode.add_child(gr_d)
|
||||
index = 1
|
||||
for entry in friend.get_int_array('gr_d', 5):
|
||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
||||
for entry in friend.get_int_array("gr_d", 5):
|
||||
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||
index = index + 1
|
||||
|
||||
# Play area
|
||||
areas = profile.get_int_array('play_area', 55)
|
||||
play_area = Node.void('play_area')
|
||||
areas = profile.get_int_array("play_area", 55)
|
||||
play_area = Node.void("play_area")
|
||||
root.add_child(play_area)
|
||||
for i in range(len(areas)):
|
||||
play_area.set_attribute(f'play_cnt{i}', str(areas[i]))
|
||||
play_area.set_attribute(f"play_cnt{i}", str(areas[i]))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
play_stats = self.get_play_statistics(userid)
|
||||
|
||||
# Grab last node and accessories so we can make decisions based on type
|
||||
last = request.child('last')
|
||||
lastdict = newprofile.get_dict('last')
|
||||
mode = int(last.attribute('mode'))
|
||||
style = int(last.attribute('style'))
|
||||
last = request.child("last")
|
||||
lastdict = newprofile.get_dict("last")
|
||||
mode = int(last.attribute("mode"))
|
||||
style = int(last.attribute("style"))
|
||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||
|
||||
# Drill rankings
|
||||
title = request.child('title')
|
||||
title_gr = request.child('title_gr')
|
||||
titledict = newprofile.get_dict('title')
|
||||
title_grdict = newprofile.get_dict('title_gr')
|
||||
title = request.child("title")
|
||||
title_gr = request.child("title_gr")
|
||||
titledict = newprofile.get_dict("title")
|
||||
title_grdict = newprofile.get_dict("title_gr")
|
||||
|
||||
# Groove radar level ups
|
||||
gr = request.child('gr')
|
||||
gr = request.child("gr")
|
||||
|
||||
# Set the correct values depending on if we're single or double play
|
||||
if is_dp:
|
||||
play_stats.increment_int('double_plays')
|
||||
play_stats.increment_int("double_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_d',
|
||||
"gr_d",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('d', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
title_grdict.replace_int("d", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
else:
|
||||
play_stats.increment_int('single_plays')
|
||||
play_stats.increment_int("single_plays")
|
||||
if gr is not None:
|
||||
newprofile.replace_int_array(
|
||||
'gr_s',
|
||||
"gr_s",
|
||||
5,
|
||||
[
|
||||
intish(gr.attribute('gr1')),
|
||||
intish(gr.attribute('gr2')),
|
||||
intish(gr.attribute('gr3')),
|
||||
intish(gr.attribute('gr4')),
|
||||
intish(gr.attribute('gr5')),
|
||||
intish(gr.attribute("gr1")),
|
||||
intish(gr.attribute("gr2")),
|
||||
intish(gr.attribute("gr3")),
|
||||
intish(gr.attribute("gr4")),
|
||||
intish(gr.attribute("gr5")),
|
||||
],
|
||||
)
|
||||
if title is not None:
|
||||
titledict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title', titledict)
|
||||
titledict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title", titledict)
|
||||
if title_gr is not None:
|
||||
title_grdict.replace_int('s', title.value)
|
||||
newprofile.replace_dict('title_gr', title_grdict)
|
||||
play_stats.increment_int(f'cnt_m{mode}')
|
||||
title_grdict.replace_int("s", title.value)
|
||||
newprofile.replace_dict("title_gr", title_grdict)
|
||||
play_stats.increment_int(f"cnt_m{mode}")
|
||||
|
||||
# Result stars
|
||||
result_star = request.child('result_star')
|
||||
result_star = request.child("result_star")
|
||||
if result_star is not None:
|
||||
newprofile.replace_int_array(
|
||||
'result_stars',
|
||||
"result_stars",
|
||||
9,
|
||||
[
|
||||
intish(result_star.attribute('slot1')),
|
||||
intish(result_star.attribute('slot2')),
|
||||
intish(result_star.attribute('slot3')),
|
||||
intish(result_star.attribute('slot4')),
|
||||
intish(result_star.attribute('slot5')),
|
||||
intish(result_star.attribute('slot6')),
|
||||
intish(result_star.attribute('slot7')),
|
||||
intish(result_star.attribute('slot8')),
|
||||
intish(result_star.attribute('slot9')),
|
||||
intish(result_star.attribute("slot1")),
|
||||
intish(result_star.attribute("slot2")),
|
||||
intish(result_star.attribute("slot3")),
|
||||
intish(result_star.attribute("slot4")),
|
||||
intish(result_star.attribute("slot5")),
|
||||
intish(result_star.attribute("slot6")),
|
||||
intish(result_star.attribute("slot7")),
|
||||
intish(result_star.attribute("slot8")),
|
||||
intish(result_star.attribute("slot9")),
|
||||
],
|
||||
)
|
||||
|
||||
# Target stuff
|
||||
target = request.child('target')
|
||||
target = request.child("target")
|
||||
if target is not None:
|
||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
||||
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||
|
||||
# Update last attributes
|
||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Grab character options
|
||||
chara = request.child('chara')
|
||||
chara = request.child("chara")
|
||||
if chara is not None:
|
||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
||||
chara_opt = request.child('chara_opt')
|
||||
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||
chara_opt = request.child("chara_opt")
|
||||
if chara_opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('chara_opt', 96, chara_opt.value[:96])
|
||||
newprofile.replace_int_array("chara_opt", 96, chara_opt.value[:96])
|
||||
|
||||
# Options
|
||||
opt = request.child('opt')
|
||||
opt = request.child("opt")
|
||||
if opt is not None:
|
||||
# A bug in old versions of AVS returns the wrong number for set
|
||||
newprofile.replace_int_array('opt', 16, opt.value[:16])
|
||||
newprofile.replace_int_array("opt", 16, opt.value[:16])
|
||||
|
||||
# Experience and stars
|
||||
exp = request.child_value('exp')
|
||||
exp = request.child_value("exp")
|
||||
if exp is not None:
|
||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
||||
star = request.child_value('star')
|
||||
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||
star = request.child_value("star")
|
||||
if star is not None:
|
||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
||||
star_c = request.child_value('star_c')
|
||||
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||
star_c = request.child_value("star_c")
|
||||
if star_c is not None:
|
||||
newprofile.replace_int('star_c', newprofile.get_int('star_c') + exp)
|
||||
newprofile.replace_int("star_c", newprofile.get_int("star_c") + exp)
|
||||
|
||||
# Update game flags
|
||||
for child in request.children:
|
||||
if child.name != 'flag':
|
||||
if child.name != "flag":
|
||||
continue
|
||||
try:
|
||||
value = int(child.attribute('data'))
|
||||
offset = int(child.attribute('no'))
|
||||
value = int(child.attribute("data"))
|
||||
offset = int(child.attribute("no"))
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
flags = newprofile.get_int_array('flag', 256, [1] * 256)
|
||||
flags = newprofile.get_int_array("flag", 256, [1] * 256)
|
||||
if offset < 0 or offset >= len(flags):
|
||||
continue
|
||||
flags[offset] = value
|
||||
newprofile.replace_int_array('flag', 256, flags)
|
||||
newprofile.replace_int_array("flag", 256, flags)
|
||||
|
||||
# Workout mode support
|
||||
newweight = -1
|
||||
oldweight = newprofile.get_int('weight')
|
||||
oldweight = newprofile.get_int("weight")
|
||||
for child in request.children:
|
||||
if child.name != 'weight':
|
||||
if child.name != "weight":
|
||||
continue
|
||||
newweight = child.value
|
||||
if newweight < 0:
|
||||
@ -700,14 +727,14 @@ class DDRX3(
|
||||
# Either update or unset the weight depending on the game
|
||||
if newweight == 0:
|
||||
# Weight is unset or we declined to use this feature, remove from profile
|
||||
if 'weight' in newprofile:
|
||||
del newprofile['weight']
|
||||
if "weight" in newprofile:
|
||||
del newprofile["weight"]
|
||||
else:
|
||||
# Weight has been set or previously retrieved, we should save calories
|
||||
newprofile.replace_int('weight', newweight)
|
||||
newprofile.replace_int("weight", newweight)
|
||||
total = 0
|
||||
for child in request.children:
|
||||
if child.name != 'calory':
|
||||
if child.name != "calory":
|
||||
continue
|
||||
total += child.value
|
||||
self.data.local.user.put_time_based_achievement(
|
||||
@ -715,10 +742,10 @@ class DDRX3(
|
||||
self.version,
|
||||
userid,
|
||||
0,
|
||||
'workout',
|
||||
"workout",
|
||||
{
|
||||
'calories': total,
|
||||
'weight': newweight,
|
||||
"calories": total,
|
||||
"weight": newweight,
|
||||
},
|
||||
)
|
||||
|
||||
@ -726,7 +753,7 @@ class DDRX3(
|
||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
for link in links:
|
||||
if link.type[:7] != 'friend_':
|
||||
if link.type[:7] != "friend_":
|
||||
continue
|
||||
|
||||
pos = int(link.type[7:])
|
||||
@ -735,11 +762,11 @@ class DDRX3(
|
||||
# Save any rivals that were added/removed/changed
|
||||
newfriends = oldfriends[:]
|
||||
for child in request.children:
|
||||
if child.name != 'friend':
|
||||
if child.name != "friend":
|
||||
continue
|
||||
|
||||
code = int(child.attribute('code'))
|
||||
pos = int(child.attribute('pos'))
|
||||
code = int(child.attribute("code"))
|
||||
pos = int(child.attribute("pos"))
|
||||
|
||||
if pos >= 0 and pos < 10:
|
||||
if code == 0:
|
||||
@ -747,7 +774,9 @@ class DDRX3(
|
||||
newfriends[pos] = None
|
||||
else:
|
||||
# Try looking up the userid
|
||||
newfriends[pos] = self.data.remote.user.from_extid(self.game, self.version, code)
|
||||
newfriends[pos] = self.data.remote.user.from_extid(
|
||||
self.game, self.version, code
|
||||
)
|
||||
|
||||
# Diff the set of links to determine updates
|
||||
for i in range(10):
|
||||
@ -760,7 +789,7 @@ class DDRX3(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
elif oldfriends[i] is None:
|
||||
@ -769,7 +798,7 @@ class DDRX3(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
@ -779,24 +808,24 @@ class DDRX3(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
oldfriends[i],
|
||||
)
|
||||
self.data.local.user.put_link(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
f'friend_{i}',
|
||||
f"friend_{i}",
|
||||
newfriends[i],
|
||||
{},
|
||||
)
|
||||
|
||||
# Play area counter
|
||||
shop_area = int(request.attribute('shop_area'))
|
||||
shop_area = int(request.attribute("shop_area"))
|
||||
if shop_area >= 0 and shop_area < 55:
|
||||
areas = newprofile.get_int_array('play_area', 55)
|
||||
areas = newprofile.get_int_array("play_area", 55)
|
||||
areas[shop_area] = areas[shop_area] + 1
|
||||
newprofile.replace_int_array('play_area', 55, areas)
|
||||
newprofile.replace_int_array("play_area", 55, areas)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid, play_stats)
|
||||
|
@ -48,12 +48,17 @@ class DDRFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['HDX', 'JDX', 'KDX', 'MDX']:
|
||||
for gamecode in ["HDX", "JDX", "KDX", "MDX"]:
|
||||
Base.register(gamecode, DDRFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date < 2014051200:
|
||||
return VersionConstants.DDR_2013
|
||||
@ -65,20 +70,20 @@ class DDRFactory(Factory):
|
||||
return VersionConstants.DDR_A20
|
||||
return None
|
||||
|
||||
if model.gamecode == 'HDX':
|
||||
if model.gamecode == "HDX":
|
||||
return DDRX(data, config, model)
|
||||
if model.gamecode == 'JDX':
|
||||
if model.gamecode == "JDX":
|
||||
return DDRX2(data, config, model)
|
||||
if model.gamecode == 'KDX':
|
||||
if model.gamecode == "KDX":
|
||||
return DDRX3(data, config, model)
|
||||
if model.gamecode == 'MDX':
|
||||
if model.gamecode == "MDX":
|
||||
if model.version is None:
|
||||
if parentmodel is None:
|
||||
return None
|
||||
|
||||
# We have no way to tell apart newer versions. However, we can make
|
||||
# an educated guess if we happen to be summoned for old profile lookup.
|
||||
if parentmodel.gamecode not in ['HDX', 'JDX', 'KDX', 'MDX']:
|
||||
if parentmodel.gamecode not in ["HDX", "JDX", "KDX", "MDX"]:
|
||||
return None
|
||||
|
||||
parentversion = version_from_date(parentmodel.version)
|
||||
|
@ -7,7 +7,7 @@ from bemani.common import VersionConstants
|
||||
|
||||
class DDRX(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution X'
|
||||
name: str = "DanceDanceRevolution X"
|
||||
version: int = VersionConstants.DDR_X
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -16,7 +16,7 @@ class DDRX(DDRBase):
|
||||
|
||||
class DDRSuperNova2(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution SuperNova 2'
|
||||
name: str = "DanceDanceRevolution SuperNova 2"
|
||||
version: int = VersionConstants.DDR_SUPERNOVA_2
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -25,7 +25,7 @@ class DDRSuperNova2(DDRBase):
|
||||
|
||||
class DDRSuperNova(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution SuperNova'
|
||||
name: str = "DanceDanceRevolution SuperNova"
|
||||
version: int = VersionConstants.DDR_SUPERNOVA
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -34,7 +34,7 @@ class DDRSuperNova(DDRBase):
|
||||
|
||||
class DDRExtreme(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution Extreme'
|
||||
name: str = "DanceDanceRevolution Extreme"
|
||||
version: int = VersionConstants.DDR_EXTREME
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -43,7 +43,7 @@ class DDRExtreme(DDRBase):
|
||||
|
||||
class DDR7thMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 7thMix'
|
||||
name: str = "DanceDanceRevolution 7thMix"
|
||||
version: int = VersionConstants.DDR_7THMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -52,7 +52,7 @@ class DDR7thMix(DDRBase):
|
||||
|
||||
class DDR6thMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 6thMix'
|
||||
name: str = "DanceDanceRevolution 6thMix"
|
||||
version: int = VersionConstants.DDR_6THMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -61,7 +61,7 @@ class DDR6thMix(DDRBase):
|
||||
|
||||
class DDR5thMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 5thMix'
|
||||
name: str = "DanceDanceRevolution 5thMix"
|
||||
version: int = VersionConstants.DDR_5THMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -70,7 +70,7 @@ class DDR5thMix(DDRBase):
|
||||
|
||||
class DDR4thMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 4thMix'
|
||||
name: str = "DanceDanceRevolution 4thMix"
|
||||
version: int = VersionConstants.DDR_4THMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -79,7 +79,7 @@ class DDR4thMix(DDRBase):
|
||||
|
||||
class DDR3rdMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 3rdMix'
|
||||
name: str = "DanceDanceRevolution 3rdMix"
|
||||
version: int = VersionConstants.DDR_3RDMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -88,7 +88,7 @@ class DDR3rdMix(DDRBase):
|
||||
|
||||
class DDR2ndMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 2ndMix'
|
||||
name: str = "DanceDanceRevolution 2ndMix"
|
||||
version: int = VersionConstants.DDR_2NDMIX
|
||||
|
||||
def previous_version(self) -> Optional[DDRBase]:
|
||||
@ -97,5 +97,5 @@ class DDR2ndMix(DDRBase):
|
||||
|
||||
class DDR1stMix(DDRBase):
|
||||
|
||||
name: str = 'DanceDanceRevolution 1stMix'
|
||||
name: str = "DanceDanceRevolution 1stMix"
|
||||
version: int = VersionConstants.DDR_1STMIX
|
||||
|
@ -61,7 +61,7 @@ class Dispatch:
|
||||
"""
|
||||
self.log("Received request:\n{}", tree)
|
||||
|
||||
if tree.name != 'call':
|
||||
if tree.name != "call":
|
||||
# Invalid request
|
||||
self.log("Invalid root node {}", tree.name)
|
||||
return None
|
||||
@ -71,15 +71,17 @@ class Dispatch:
|
||||
self.log("Invalid number of children for root node")
|
||||
return None
|
||||
|
||||
modelstring = tree.attribute('model')
|
||||
modelstring = tree.attribute("model")
|
||||
model = Model.from_modelstring(modelstring)
|
||||
pcbid = tree.attribute('srcid')
|
||||
pcbid = tree.attribute("srcid")
|
||||
|
||||
# If we are enforcing, bail out if we don't recognize thie ID
|
||||
pcb = self.__data.local.machine.get_machine(pcbid)
|
||||
if self.__config.server.enforce_pcbid and pcb is None:
|
||||
self.log("Unrecognized PCBID {}", pcbid)
|
||||
raise UnrecognizedPCBIDException(pcbid, modelstring, self.__config.client.address)
|
||||
raise UnrecognizedPCBIDException(
|
||||
pcbid, modelstring, self.__config.client.address
|
||||
)
|
||||
|
||||
# If we don't have a Machine, but we aren't enforcing, we must create it
|
||||
if pcb is None:
|
||||
@ -88,9 +90,9 @@ class Dispatch:
|
||||
request = tree.children[0]
|
||||
|
||||
config = self.__config.clone()
|
||||
config['machine'] = {
|
||||
'pcbid': pcbid,
|
||||
'arcade': pcb.arcade,
|
||||
config["machine"] = {
|
||||
"pcbid": pcbid,
|
||||
"arcade": pcb.arcade,
|
||||
}
|
||||
|
||||
# If the machine we looked up is in an arcade, override the global
|
||||
@ -98,22 +100,29 @@ class Dispatch:
|
||||
if pcb.arcade is not None:
|
||||
arcade = self.__data.local.machine.get_arcade(pcb.arcade)
|
||||
if arcade is not None:
|
||||
config['paseli']['enabled'] = arcade.data.get_bool('paseli_enabled')
|
||||
config['paseli']['infinite'] = arcade.data.get_bool('paseli_infinite')
|
||||
if arcade.data.get_bool('mask_services_url'):
|
||||
config["paseli"]["enabled"] = arcade.data.get_bool("paseli_enabled")
|
||||
config["paseli"]["infinite"] = arcade.data.get_bool("paseli_infinite")
|
||||
if arcade.data.get_bool("mask_services_url"):
|
||||
# Mask the address, no matter what the server settings are
|
||||
config['server']['uri'] = None
|
||||
config["server"]["uri"] = None
|
||||
|
||||
game = Base.create(self.__data, config, model)
|
||||
method = request.attribute('method')
|
||||
method = request.attribute("method")
|
||||
response = None
|
||||
|
||||
# If we are enforcing, make sure the PCBID isn't specified to be
|
||||
# game-specific
|
||||
if config.server.enforce_pcbid and pcb.game is not None:
|
||||
if pcb.game != game.game:
|
||||
self.log("PCBID {} assigned to game {}, but connected from game {}", pcbid, pcb.game, game.game)
|
||||
raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address)
|
||||
self.log(
|
||||
"PCBID {} assigned to game {}, but connected from game {}",
|
||||
pcbid,
|
||||
pcb.game,
|
||||
game.game,
|
||||
)
|
||||
raise UnrecognizedPCBIDException(
|
||||
pcbid, modelstring, config.client.address
|
||||
)
|
||||
if pcb.version is not None:
|
||||
if pcb.version > 0 and pcb.version != game.version:
|
||||
self.log(
|
||||
@ -124,7 +133,9 @@ class Dispatch:
|
||||
game.game,
|
||||
game.version,
|
||||
)
|
||||
raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address)
|
||||
raise UnrecognizedPCBIDException(
|
||||
pcbid, modelstring, config.client.address
|
||||
)
|
||||
if pcb.version < 0 and (-pcb.version) < game.version:
|
||||
self.log(
|
||||
"PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}",
|
||||
@ -134,11 +145,13 @@ class Dispatch:
|
||||
game.game,
|
||||
game.version,
|
||||
)
|
||||
raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address)
|
||||
raise UnrecognizedPCBIDException(
|
||||
pcbid, modelstring, config.client.address
|
||||
)
|
||||
|
||||
# First, try to handle with specific service/method function
|
||||
try:
|
||||
handler = getattr(game, f'handle_{request.name}_{method}_request')
|
||||
handler = getattr(game, f"handle_{request.name}_{method}_request")
|
||||
except AttributeError:
|
||||
handler = None
|
||||
if handler is not None:
|
||||
@ -147,7 +160,7 @@ class Dispatch:
|
||||
if response is None:
|
||||
# Now, try to pass it off to a generic service handler
|
||||
try:
|
||||
handler = getattr(game, f'handle_{request.name}_requests')
|
||||
handler = getattr(game, f"handle_{request.name}_requests")
|
||||
except AttributeError:
|
||||
handler = None
|
||||
if handler is not None:
|
||||
@ -159,12 +172,12 @@ class Dispatch:
|
||||
return None
|
||||
|
||||
# Make sure we have a status value if one wasn't provided
|
||||
if 'status' not in response.attributes:
|
||||
response.set_attribute('status', str(Status.SUCCESS))
|
||||
if "status" not in response.attributes:
|
||||
response.set_attribute("status", str(Status.SUCCESS))
|
||||
|
||||
root = Node.void('response')
|
||||
root = Node.void("response")
|
||||
root.add_child(response)
|
||||
root.set_attribute('dstid', pcbid)
|
||||
root.set_attribute("dstid", pcbid)
|
||||
|
||||
self.log("Sending response:\n{}", root)
|
||||
|
||||
|
@ -11,13 +11,13 @@ class EventLogHandler(Base):
|
||||
|
||||
def handle_eventlog_write_request(self, request: Node) -> Node:
|
||||
# Just turn off further logging
|
||||
gamesession = request.child_value('data/gamesession')
|
||||
gamesession = request.child_value("data/gamesession")
|
||||
if gamesession < 0:
|
||||
gamesession = random.randint(1, 1000000)
|
||||
|
||||
root = Node.void('eventlog')
|
||||
root.add_child(Node.s64('gamesession', gamesession))
|
||||
root.add_child(Node.s32('logsendflg', 0))
|
||||
root.add_child(Node.s32('logerrlevel', 0))
|
||||
root.add_child(Node.s32('evtidnosendflg', 0))
|
||||
root = Node.void("eventlog")
|
||||
root.add_child(Node.s64("gamesession", gamesession))
|
||||
root.add_child(Node.s32("logsendflg", 0))
|
||||
root.add_child(Node.s32("logerrlevel", 0))
|
||||
root.add_child(Node.s32("evtidnosendflg", 0))
|
||||
return root
|
||||
|
@ -5,7 +5,14 @@ from typing_extensions import Final
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
||||
from bemani.common import Profile, ValidatedDict, Model, GameConstants, DBConstants, Parallel
|
||||
from bemani.common import (
|
||||
Profile,
|
||||
ValidatedDict,
|
||||
Model,
|
||||
GameConstants,
|
||||
DBConstants,
|
||||
Parallel,
|
||||
)
|
||||
from bemani.data import Config, Data, Score, Machine, UserID
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -62,8 +69,8 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
DAN_RANK_CHUDEN: Final[int] = DBConstants.IIDX_DAN_RANK_CHUDEN
|
||||
DAN_RANK_KAIDEN: Final[int] = DBConstants.IIDX_DAN_RANK_KAIDEN
|
||||
|
||||
DAN_RANKING_SINGLE: Final[str] = 'sgrade'
|
||||
DAN_RANKING_DOUBLE: Final[str] = 'dgrade'
|
||||
DAN_RANKING_SINGLE: Final[str] = "sgrade"
|
||||
DAN_RANKING_DOUBLE: Final[str] = "dgrade"
|
||||
|
||||
GHOST_TYPE_NONE: Final[int] = 0
|
||||
GHOST_TYPE_RIVAL: Final[int] = 100
|
||||
@ -78,12 +85,12 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
# Return the local2 service so that Copula and above will send certain packets.
|
||||
extra_services: List[str] = [
|
||||
'local2',
|
||||
"local2",
|
||||
]
|
||||
|
||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||
super().__init__(data, config, model)
|
||||
if model.rev == 'X':
|
||||
if model.rev == "X":
|
||||
self.omnimix = True
|
||||
else:
|
||||
self.omnimix = False
|
||||
@ -94,7 +101,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||
return self.version
|
||||
|
||||
def previous_version(self) -> Optional['IIDXBase']:
|
||||
def previous_version(self) -> Optional["IIDXBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -106,9 +113,11 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('pc')
|
||||
return Node.void("pc")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -136,7 +145,9 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
def new_profile_by_refid(self, refid: Optional[str], name: Optional[str], pid: Optional[int]) -> Profile:
|
||||
def new_profile_by_refid(
|
||||
self, refid: Optional[str], name: Optional[str], pid: Optional[int]
|
||||
) -> Profile:
|
||||
"""
|
||||
Given a RefID and an optional name, create a profile and then return
|
||||
that newly created profile.
|
||||
@ -145,7 +156,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = 'なし'
|
||||
name = "なし"
|
||||
if pid is None:
|
||||
pid = self.get_machine_region()
|
||||
|
||||
@ -156,10 +167,10 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
'pid': pid,
|
||||
'settings': {
|
||||
'flags': 223 # Default to turning on all optional folders
|
||||
"name": name,
|
||||
"pid": pid,
|
||||
"settings": {
|
||||
"flags": 223 # Default to turning on all optional folders
|
||||
},
|
||||
},
|
||||
)
|
||||
@ -192,8 +203,8 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
def get_clear_rates(
|
||||
self,
|
||||
songid: Optional[int]=None,
|
||||
songchart: Optional[int]=None,
|
||||
songid: Optional[int] = None,
|
||||
songchart: Optional[int] = None,
|
||||
) -> Dict[int, Dict[int, Dict[str, int]]]:
|
||||
"""
|
||||
Returns a dictionary similar to the following:
|
||||
@ -208,24 +219,26 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
},
|
||||
}
|
||||
"""
|
||||
all_attempts, remote_attempts = Parallel.execute([
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=songid,
|
||||
songchart=songchart,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=songid,
|
||||
songchart=songchart,
|
||||
),
|
||||
])
|
||||
all_attempts, remote_attempts = Parallel.execute(
|
||||
[
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=songid,
|
||||
songchart=songchart,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=songid,
|
||||
songchart=songchart,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||
for (_, attempt) in all_attempts:
|
||||
if attempt.data.get_int('clear_status') == self.CLEAR_STATUS_NO_PLAY:
|
||||
if attempt.data.get_int("clear_status") == self.CLEAR_STATUS_NO_PLAY:
|
||||
# This attempt was outside of the clear infra, so don't bother with it.
|
||||
continue
|
||||
|
||||
@ -234,24 +247,33 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
attempts[attempt.id] = {}
|
||||
if attempt.chart not in attempts[attempt.id]:
|
||||
attempts[attempt.id][attempt.chart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
'fcs': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
"fcs": 0,
|
||||
}
|
||||
|
||||
# We saw an attempt, keep the total attempts in sync.
|
||||
attempts[attempt.id][attempt.chart]['total'] = attempts[attempt.id][attempt.chart]['total'] + 1
|
||||
attempts[attempt.id][attempt.chart]["total"] = (
|
||||
attempts[attempt.id][attempt.chart]["total"] + 1
|
||||
)
|
||||
|
||||
if attempt.data.get_int('clear_status', self.CLEAR_STATUS_FAILED) == self.CLEAR_STATUS_FAILED:
|
||||
if (
|
||||
attempt.data.get_int("clear_status", self.CLEAR_STATUS_FAILED)
|
||||
== self.CLEAR_STATUS_FAILED
|
||||
):
|
||||
# This attempt was a failure, so don't count it against clears of full combos
|
||||
continue
|
||||
|
||||
# It was at least a clear
|
||||
attempts[attempt.id][attempt.chart]['clears'] = attempts[attempt.id][attempt.chart]['clears'] + 1
|
||||
attempts[attempt.id][attempt.chart]["clears"] = (
|
||||
attempts[attempt.id][attempt.chart]["clears"] + 1
|
||||
)
|
||||
|
||||
if attempt.data.get_int('clear_status') == self.CLEAR_STATUS_FULL_COMBO:
|
||||
if attempt.data.get_int("clear_status") == self.CLEAR_STATUS_FULL_COMBO:
|
||||
# This was a full combo clear, so it also counts here
|
||||
attempts[attempt.id][attempt.chart]['fcs'] = attempts[attempt.id][attempt.chart]['fcs'] + 1
|
||||
attempts[attempt.id][attempt.chart]["fcs"] = (
|
||||
attempts[attempt.id][attempt.chart]["fcs"] + 1
|
||||
)
|
||||
|
||||
# Merge in remote attempts
|
||||
for songid in remote_attempts:
|
||||
@ -261,14 +283,20 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
for songchart in remote_attempts[songid]:
|
||||
if songchart not in attempts[songid]:
|
||||
attempts[songid][songchart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
'fcs': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
"fcs": 0,
|
||||
}
|
||||
|
||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
||||
attempts[songid][songchart]['fcs'] += remote_attempts[songid][songchart]['combos']
|
||||
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["plays"]
|
||||
attempts[songid][songchart]["clears"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["clears"]
|
||||
attempts[songid][songchart]["fcs"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["combos"]
|
||||
|
||||
# If requesting a specific song/chart, make sure its in the dict
|
||||
if songid is not None:
|
||||
@ -278,9 +306,9 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
if songchart is not None:
|
||||
if songchart not in attempts[songid]:
|
||||
attempts[songid][songchart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
'fcs': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
"fcs": 0,
|
||||
}
|
||||
|
||||
return attempts
|
||||
@ -339,26 +367,30 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
oldscore = None
|
||||
|
||||
# Score history is verbatum, instead of highest score
|
||||
history = ValidatedDict({
|
||||
'clear_status': clear_status,
|
||||
'miss_count': miss_count,
|
||||
})
|
||||
history = ValidatedDict(
|
||||
{
|
||||
"clear_status": clear_status,
|
||||
"miss_count": miss_count,
|
||||
}
|
||||
)
|
||||
old_ex_score = ex_score
|
||||
|
||||
if ghost is not None:
|
||||
history['ghost'] = ghost
|
||||
history["ghost"] = ghost
|
||||
|
||||
if oldscore is None:
|
||||
# If it is a new score, create a new dictionary to add to
|
||||
scoredata = ValidatedDict({
|
||||
'clear_status': clear_status,
|
||||
'pgreats': pgreats,
|
||||
'greats': greats,
|
||||
})
|
||||
scoredata = ValidatedDict(
|
||||
{
|
||||
"clear_status": clear_status,
|
||||
"pgreats": pgreats,
|
||||
"greats": greats,
|
||||
}
|
||||
)
|
||||
if miss_count != -1:
|
||||
scoredata.replace_int('miss_count', miss_count)
|
||||
scoredata.replace_int("miss_count", miss_count)
|
||||
if ghost is not None:
|
||||
scoredata['ghost'] = ghost
|
||||
scoredata["ghost"] = ghost
|
||||
raised = True
|
||||
highscore = True
|
||||
else:
|
||||
@ -367,21 +399,25 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
highscore = ex_score >= oldscore.points
|
||||
ex_score = max(ex_score, oldscore.points)
|
||||
scoredata = oldscore.data
|
||||
scoredata.replace_int('clear_status', max(scoredata.get_int('clear_status'), clear_status))
|
||||
scoredata.replace_int(
|
||||
"clear_status", max(scoredata.get_int("clear_status"), clear_status)
|
||||
)
|
||||
if miss_count != -1:
|
||||
if scoredata.get_int('miss_count', -1) == -1:
|
||||
scoredata.replace_int('miss_count', miss_count)
|
||||
if scoredata.get_int("miss_count", -1) == -1:
|
||||
scoredata.replace_int("miss_count", miss_count)
|
||||
else:
|
||||
scoredata.replace_int('miss_count', min(scoredata.get_int('miss_count'), miss_count))
|
||||
scoredata.replace_int(
|
||||
"miss_count", min(scoredata.get_int("miss_count"), miss_count)
|
||||
)
|
||||
if raised:
|
||||
scoredata.replace_int('pgreats', pgreats)
|
||||
scoredata.replace_int('greats', greats)
|
||||
scoredata.replace_int("pgreats", pgreats)
|
||||
scoredata.replace_int("greats", greats)
|
||||
if ghost is not None:
|
||||
scoredata.replace_bytes('ghost', ghost)
|
||||
scoredata.replace_bytes("ghost", ghost)
|
||||
|
||||
if shop is not None:
|
||||
history.replace_int('shop', shop)
|
||||
scoredata.replace_int('shop', shop)
|
||||
history.replace_int("shop", shop)
|
||||
scoredata.replace_int("shop", shop)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
@ -477,42 +513,41 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
)
|
||||
if dan_score is None:
|
||||
dan_score = ValidatedDict()
|
||||
dan_score.replace_int('percent', max(percent, dan_score.get_int('percent')))
|
||||
dan_score.replace_int('stages_cleared', max(stages_cleared, dan_score.get_int('stages_cleared')))
|
||||
dan_score.replace_int("percent", max(percent, dan_score.get_int("percent")))
|
||||
dan_score.replace_int(
|
||||
"stages_cleared", max(stages_cleared, dan_score.get_int("stages_cleared"))
|
||||
)
|
||||
self.data.local.user.put_achievement(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
rank,
|
||||
dantype,
|
||||
dan_score
|
||||
self.game, self.version, userid, rank, dantype, dan_score
|
||||
)
|
||||
|
||||
def db_to_game_status(self, db_status: int) -> int:
|
||||
"""
|
||||
Given a DB status, translate to a game clear status.
|
||||
"""
|
||||
raise Exception('Implement in specific game class!')
|
||||
raise Exception("Implement in specific game class!")
|
||||
|
||||
def game_to_db_status(self, game_status: int) -> int:
|
||||
"""
|
||||
Given a game clear status, translate to DB status.
|
||||
"""
|
||||
raise Exception('Implement in specific game class!')
|
||||
raise Exception("Implement in specific game class!")
|
||||
|
||||
def game_to_db_chart(self, game_chart: int) -> int:
|
||||
"""
|
||||
Given a game's chart for a song, return the chart as defined above.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def db_to_game_chart(self, db_chart: int) -> int:
|
||||
"""
|
||||
Given a chart as defined above, return the game's chart constant.
|
||||
"""
|
||||
raise Exception('Implement in sub-class!')
|
||||
raise Exception("Implement in sub-class!")
|
||||
|
||||
def make_score_struct(self, scores: List[Score], cltype: int, index: int) -> List[List[int]]:
|
||||
def make_score_struct(
|
||||
self, scores: List[Score], cltype: int, index: int
|
||||
) -> List[List[int]]:
|
||||
scorestruct: Dict[int, List[int]] = {}
|
||||
|
||||
for score in scores:
|
||||
@ -560,9 +595,11 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
-1, # Miss count another,
|
||||
]
|
||||
|
||||
scorestruct[musicid][chartindex + 2] = self.db_to_game_status(score.data.get_int('clear_status'))
|
||||
scorestruct[musicid][chartindex + 2] = self.db_to_game_status(
|
||||
score.data.get_int("clear_status")
|
||||
)
|
||||
scorestruct[musicid][chartindex + 5] = score.points
|
||||
scorestruct[musicid][chartindex + 8] = score.data.get_int('miss_count', -1)
|
||||
scorestruct[musicid][chartindex + 8] = score.data.get_int("miss_count", -1)
|
||||
|
||||
return [scorestruct[s] for s in scorestruct]
|
||||
|
||||
@ -577,10 +614,12 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
if chart != self.CHART_TYPE_B7:
|
||||
continue
|
||||
|
||||
scorelist.append([
|
||||
musicid,
|
||||
self.db_to_game_status(score.data.get_int('clear_status')),
|
||||
])
|
||||
scorelist.append(
|
||||
[
|
||||
musicid,
|
||||
self.db_to_game_status(score.data.get_int("clear_status")),
|
||||
]
|
||||
)
|
||||
|
||||
return scorelist
|
||||
|
||||
@ -597,7 +636,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
# Sum up for each bucket
|
||||
for score in scores:
|
||||
ghost = score.data.get_bytes('ghost')
|
||||
ghost = score.data.get_bytes("ghost")
|
||||
for i in range(len(ghost)):
|
||||
total_ghost[i] = total_ghost[i] + ghost[i]
|
||||
count = count + 1
|
||||
@ -624,16 +663,16 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
delta_ghost = [total_ghost[i] - reference_ghost[i] for i in range(ghost_length)]
|
||||
|
||||
# Return averages
|
||||
return new_ex_score, struct.pack('b' * ghost_length, *delta_ghost)
|
||||
return new_ex_score, struct.pack("b" * ghost_length, *delta_ghost)
|
||||
|
||||
def user_joined_arcade(self, machine: Machine, profile: Optional[Profile]) -> bool:
|
||||
if profile is None:
|
||||
return False
|
||||
|
||||
if 'shop_location' not in profile:
|
||||
if "shop_location" not in profile:
|
||||
return False
|
||||
|
||||
machineid = profile.get_int('shop_location')
|
||||
machineid = profile.get_int("shop_location")
|
||||
if machineid == machine.id:
|
||||
# We can short-circuit arcade lookup because their machine
|
||||
# is the current machine.
|
||||
@ -659,30 +698,39 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
if ghost_type == self.GHOST_TYPE_RIVAL:
|
||||
rival_extid = int(parameter)
|
||||
rival_userid = self.data.remote.user.from_extid(self.game, self.version, rival_extid)
|
||||
rival_userid = self.data.remote.user.from_extid(
|
||||
self.game, self.version, rival_extid
|
||||
)
|
||||
if rival_userid is not None:
|
||||
rival_profile = self.get_profile(rival_userid)
|
||||
rival_score = self.data.remote.music.get_score(self.game, self.music_version, rival_userid, musicid, chart)
|
||||
rival_score = self.data.remote.music.get_score(
|
||||
self.game, self.music_version, rival_userid, musicid, chart
|
||||
)
|
||||
if rival_score is not None and rival_profile is not None:
|
||||
ghost_score = {
|
||||
'score': rival_score.points,
|
||||
'ghost': rival_score.data.get_bytes('ghost'),
|
||||
'name': rival_profile.get_str('name'),
|
||||
'pid': rival_profile.get_int('pid'),
|
||||
"score": rival_score.points,
|
||||
"ghost": rival_score.data.get_bytes("ghost"),
|
||||
"name": rival_profile.get_str("name"),
|
||||
"pid": rival_profile.get_int("pid"),
|
||||
}
|
||||
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP or
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP or
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE or
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP
|
||||
or ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||
or ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE
|
||||
or ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
):
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP or
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||
or ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
):
|
||||
all_scores = sorted(
|
||||
self.data.local.music.get_all_scores(game=self.game, version=self.music_version, songid=musicid, songchart=chart),
|
||||
self.data.local.music.get_all_scores(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=musicid,
|
||||
songchart=chart,
|
||||
),
|
||||
key=lambda s: s[1].points,
|
||||
reverse=True,
|
||||
)
|
||||
@ -698,30 +746,38 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
0,
|
||||
)
|
||||
|
||||
if 'shop_location' in my_profile:
|
||||
shop_id = my_profile.get_int('shop_location')
|
||||
if "shop_location" in my_profile:
|
||||
shop_id = my_profile.get_int("shop_location")
|
||||
machine = self.get_machine_by_id(shop_id)
|
||||
else:
|
||||
machine = None
|
||||
|
||||
if machine is not None:
|
||||
all_scores = [
|
||||
score for score in all_scores
|
||||
if self.user_joined_arcade(machine, self.get_any_profile(score[0]))
|
||||
score
|
||||
for score in all_scores
|
||||
if self.user_joined_arcade(
|
||||
machine, self.get_any_profile(score[0])
|
||||
)
|
||||
]
|
||||
else:
|
||||
# Not joined an arcade, so nobody matches our scores
|
||||
all_scores = []
|
||||
else:
|
||||
all_scores = sorted(
|
||||
self.data.remote.music.get_all_scores(game=self.game, version=self.music_version, songid=musicid, songchart=chart),
|
||||
self.data.remote.music.get_all_scores(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=musicid,
|
||||
songchart=chart,
|
||||
),
|
||||
key=lambda s: s[1].points,
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP or
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP
|
||||
or ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||
):
|
||||
for potential_top in all_scores:
|
||||
top_userid = potential_top[0]
|
||||
@ -729,28 +785,30 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
top_profile = self.get_any_profile(top_userid)
|
||||
if top_profile is not None:
|
||||
ghost_score = {
|
||||
'score': top_score.points,
|
||||
'ghost': top_score.data.get_bytes('ghost'),
|
||||
'name': top_profile.get_str('name'),
|
||||
'pid': top_profile.get_int('pid'),
|
||||
'extid': top_profile.extid,
|
||||
"score": top_score.points,
|
||||
"ghost": top_score.data.get_bytes("ghost"),
|
||||
"name": top_profile.get_str("name"),
|
||||
"pid": top_profile.get_int("pid"),
|
||||
"extid": top_profile.extid,
|
||||
}
|
||||
break
|
||||
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE or
|
||||
ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE
|
||||
or ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||
):
|
||||
average_score, delta_ghost = self.delta_score([score[1] for score in all_scores], ghost_length)
|
||||
average_score, delta_ghost = self.delta_score(
|
||||
[score[1] for score in all_scores], ghost_length
|
||||
)
|
||||
if average_score is not None and delta_ghost is not None:
|
||||
ghost_score = {
|
||||
'score': average_score,
|
||||
'ghost': bytes([0] * ghost_length),
|
||||
"score": average_score,
|
||||
"ghost": bytes([0] * ghost_length),
|
||||
}
|
||||
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_DAN_TOP or
|
||||
ghost_type == self.GHOST_TYPE_DAN_AVERAGE
|
||||
ghost_type == self.GHOST_TYPE_DAN_TOP
|
||||
or ghost_type == self.GHOST_TYPE_DAN_AVERAGE
|
||||
):
|
||||
is_dp = chart not in [
|
||||
self.CHART_TYPE_N7,
|
||||
@ -772,18 +830,28 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
if dan_rank != -1:
|
||||
all_scores = sorted(
|
||||
self.data.local.music.get_all_scores(game=self.game, version=self.music_version, songid=musicid, songchart=chart),
|
||||
self.data.local.music.get_all_scores(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=musicid,
|
||||
songchart=chart,
|
||||
),
|
||||
key=lambda s: s[1].points,
|
||||
reverse=True,
|
||||
)
|
||||
all_profiles = self.data.local.user.get_all_profiles(self.game, self.version)
|
||||
all_profiles = self.data.local.user.get_all_profiles(
|
||||
self.game, self.version
|
||||
)
|
||||
relevant_userids = {
|
||||
profile[0] for profile in all_profiles
|
||||
if profile[1].get_int(self.DAN_RANKING_DOUBLE if is_dp else self.DAN_RANKING_SINGLE) == dan_rank
|
||||
profile[0]
|
||||
for profile in all_profiles
|
||||
if profile[1].get_int(
|
||||
self.DAN_RANKING_DOUBLE if is_dp else self.DAN_RANKING_SINGLE
|
||||
)
|
||||
== dan_rank
|
||||
}
|
||||
relevant_scores = [
|
||||
score for score in all_scores
|
||||
if score[0] in relevant_userids
|
||||
score for score in all_scores if score[0] in relevant_userids
|
||||
]
|
||||
if ghost_type == self.GHOST_TYPE_DAN_TOP:
|
||||
for potential_top in relevant_scores:
|
||||
@ -792,27 +860,29 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
top_profile = self.get_any_profile(top_userid)
|
||||
if top_profile is not None:
|
||||
ghost_score = {
|
||||
'score': top_score.points,
|
||||
'ghost': top_score.data.get_bytes('ghost'),
|
||||
'name': top_profile.get_str('name'),
|
||||
'pid': top_profile.get_int('pid'),
|
||||
'extid': top_profile.extid,
|
||||
"score": top_score.points,
|
||||
"ghost": top_score.data.get_bytes("ghost"),
|
||||
"name": top_profile.get_str("name"),
|
||||
"pid": top_profile.get_int("pid"),
|
||||
"extid": top_profile.extid,
|
||||
}
|
||||
break
|
||||
|
||||
if ghost_type == self.GHOST_TYPE_DAN_AVERAGE:
|
||||
average_score, delta_ghost = self.delta_score([score[1] for score in relevant_scores], ghost_length)
|
||||
average_score, delta_ghost = self.delta_score(
|
||||
[score[1] for score in relevant_scores], ghost_length
|
||||
)
|
||||
if average_score is not None and delta_ghost is not None:
|
||||
ghost_score = {
|
||||
'score': average_score,
|
||||
'ghost': bytes([0] * ghost_length),
|
||||
"score": average_score,
|
||||
"ghost": bytes([0] * ghost_length),
|
||||
}
|
||||
|
||||
if (
|
||||
ghost_type == self.GHOST_TYPE_RIVAL_TOP or
|
||||
ghost_type == self.GHOST_TYPE_RIVAL_AVERAGE
|
||||
ghost_type == self.GHOST_TYPE_RIVAL_TOP
|
||||
or ghost_type == self.GHOST_TYPE_RIVAL_AVERAGE
|
||||
):
|
||||
rival_extids = [int(e[1:-1]) for e in parameter.split(',')]
|
||||
rival_extids = [int(e[1:-1]) for e in parameter.split(",")]
|
||||
rival_userids = [
|
||||
self.data.remote.user.from_extid(self.game, self.version, rival_extid)
|
||||
for rival_extid in rival_extids
|
||||
@ -820,8 +890,13 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
all_scores = sorted(
|
||||
[
|
||||
score for score in
|
||||
self.data.remote.music.get_all_scores(game=self.game, version=self.music_version, songid=musicid, songchart=chart)
|
||||
score
|
||||
for score in self.data.remote.music.get_all_scores(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
songid=musicid,
|
||||
songchart=chart,
|
||||
)
|
||||
if score[0] in rival_userids
|
||||
],
|
||||
key=lambda s: s[1].points,
|
||||
@ -834,20 +909,22 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
top_profile = self.get_any_profile(top_userid)
|
||||
if top_profile is not None:
|
||||
ghost_score = {
|
||||
'score': top_score.points,
|
||||
'ghost': top_score.data.get_bytes('ghost'),
|
||||
'name': top_profile.get_str('name'),
|
||||
'pid': top_profile.get_int('pid'),
|
||||
'extid': top_profile.extid,
|
||||
"score": top_score.points,
|
||||
"ghost": top_score.data.get_bytes("ghost"),
|
||||
"name": top_profile.get_str("name"),
|
||||
"pid": top_profile.get_int("pid"),
|
||||
"extid": top_profile.extid,
|
||||
}
|
||||
break
|
||||
|
||||
if ghost_type == self.GHOST_TYPE_RIVAL_AVERAGE:
|
||||
average_score, delta_ghost = self.delta_score([score[1] for score in all_scores], ghost_length)
|
||||
average_score, delta_ghost = self.delta_score(
|
||||
[score[1] for score in all_scores], ghost_length
|
||||
)
|
||||
if average_score is not None and delta_ghost is not None:
|
||||
ghost_score = {
|
||||
'score': average_score,
|
||||
'ghost': bytes([0] * ghost_length),
|
||||
"score": average_score,
|
||||
"ghost": bytes([0] * ghost_length),
|
||||
}
|
||||
|
||||
return ghost_score
|
||||
|
@ -8,7 +8,7 @@ from bemani.common import VersionConstants
|
||||
|
||||
class IIDXBistrover(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX BISTROVER'
|
||||
name: str = "Beatmania IIDX BISTROVER"
|
||||
version: int = VersionConstants.IIDX_BISTROVER
|
||||
|
||||
requires_extended_regions: bool = True
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -9,9 +9,9 @@ from bemani.data import UserID
|
||||
|
||||
class IIDXCourse(IIDXBase):
|
||||
|
||||
COURSE_TYPE_SECRET: Final[str] = 'secret_course'
|
||||
COURSE_TYPE_INTERNET_RANKING: Final[str] = 'ir_course'
|
||||
COURSE_TYPE_CLASSIC: Final[str] = 'classic_course'
|
||||
COURSE_TYPE_SECRET: Final[str] = "secret_course"
|
||||
COURSE_TYPE_INTERNET_RANKING: Final[str] = "ir_course"
|
||||
COURSE_TYPE_CLASSIC: Final[str] = "classic_course"
|
||||
|
||||
def id_and_chart_from_courseid(self, courseid: int) -> Tuple[int, int]:
|
||||
return (int(courseid / 6), courseid % 6)
|
||||
@ -57,11 +57,15 @@ class IIDXCourse(IIDXBase):
|
||||
)
|
||||
if course_score is None:
|
||||
course_score = ValidatedDict()
|
||||
course_score.replace_int('clear_status', max(clear_status, course_score.get_int('clear_status')))
|
||||
old_ex_score = (course_score.get_int('pgnum') * 2) + course_score.get_int('gnum')
|
||||
course_score.replace_int(
|
||||
"clear_status", max(clear_status, course_score.get_int("clear_status"))
|
||||
)
|
||||
old_ex_score = (course_score.get_int("pgnum") * 2) + course_score.get_int(
|
||||
"gnum"
|
||||
)
|
||||
if old_ex_score < ((pgreats * 2) + greats):
|
||||
course_score.replace_int('pgnum', pgreats)
|
||||
course_score.replace_int('gnum', greats)
|
||||
course_score.replace_int("pgnum", pgreats)
|
||||
course_score.replace_int("gnum", greats)
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
self.game,
|
||||
|
@ -70,12 +70,17 @@ class IIDXFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['JDJ', 'JDZ', 'KDZ', 'LDJ']:
|
||||
for gamecode in ["JDJ", "JDZ", "KDZ", "LDJ"]:
|
||||
Base.register(gamecode, IIDXFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date < 2013100200:
|
||||
return VersionConstants.IIDX_TRICORO
|
||||
@ -97,20 +102,20 @@ class IIDXFactory(Factory):
|
||||
return VersionConstants.IIDX_BISTROVER
|
||||
return None
|
||||
|
||||
if model.gamecode == 'JDJ':
|
||||
if model.gamecode == "JDJ":
|
||||
return IIDXSirius(data, config, model)
|
||||
if model.gamecode == 'JDZ':
|
||||
if model.gamecode == "JDZ":
|
||||
return IIDXResortAnthem(data, config, model)
|
||||
if model.gamecode == 'KDZ':
|
||||
if model.gamecode == "KDZ":
|
||||
return IIDXLincle(data, config, model)
|
||||
if model.gamecode == 'LDJ':
|
||||
if model.gamecode == "LDJ":
|
||||
if model.version is None:
|
||||
if parentmodel is None:
|
||||
return None
|
||||
|
||||
# We have no way to tell apart newer versions. However, we can make
|
||||
# an educated guess if we happen to be summoned for old profile lookup.
|
||||
if parentmodel.gamecode not in ['JDJ', 'JDZ', 'KDZ', 'LDJ']:
|
||||
if parentmodel.gamecode not in ["JDJ", "JDZ", "KDZ", "LDJ"]:
|
||||
return None
|
||||
parentversion = version_from_date(parentmodel.version)
|
||||
if parentversion == VersionConstants.IIDX_SPADA:
|
||||
|
@ -8,7 +8,7 @@ from bemani.common import VersionConstants
|
||||
|
||||
class IIDXHeroicVerse(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX HEROIC VERSE'
|
||||
name: str = "Beatmania IIDX HEROIC VERSE"
|
||||
version: int = VersionConstants.IIDX_HEROIC_VERSE
|
||||
|
||||
requires_extended_regions: bool = True
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,13 @@ from bemani.common import VersionConstants
|
||||
|
||||
class IIDX1stStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 1st style & substream'
|
||||
name: str = "Beatmania IIDX 1st style & substream"
|
||||
version: int = VersionConstants.IIDX
|
||||
|
||||
|
||||
class IIDX2ndStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 2nd style'
|
||||
name: str = "Beatmania IIDX 2nd style"
|
||||
version: int = VersionConstants.IIDX_2ND_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -22,7 +22,7 @@ class IIDX2ndStyle(IIDXBase):
|
||||
|
||||
class IIDX3rdStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 3rd style'
|
||||
name: str = "Beatmania IIDX 3rd style"
|
||||
version: int = VersionConstants.IIDX_3RD_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -31,7 +31,7 @@ class IIDX3rdStyle(IIDXBase):
|
||||
|
||||
class IIDX4thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 4th style'
|
||||
name: str = "Beatmania IIDX 4th style"
|
||||
version: int = VersionConstants.IIDX_4TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -40,7 +40,7 @@ class IIDX4thStyle(IIDXBase):
|
||||
|
||||
class IIDX5thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 5th style'
|
||||
name: str = "Beatmania IIDX 5th style"
|
||||
version: int = VersionConstants.IIDX_5TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -49,7 +49,7 @@ class IIDX5thStyle(IIDXBase):
|
||||
|
||||
class IIDX6thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 6th style'
|
||||
name: str = "Beatmania IIDX 6th style"
|
||||
version: int = VersionConstants.IIDX_6TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -58,7 +58,7 @@ class IIDX6thStyle(IIDXBase):
|
||||
|
||||
class IIDX7thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 7th style'
|
||||
name: str = "Beatmania IIDX 7th style"
|
||||
version: int = VersionConstants.IIDX_7TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -67,7 +67,7 @@ class IIDX7thStyle(IIDXBase):
|
||||
|
||||
class IIDX8thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 8th style'
|
||||
name: str = "Beatmania IIDX 8th style"
|
||||
version: int = VersionConstants.IIDX_8TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -76,7 +76,7 @@ class IIDX8thStyle(IIDXBase):
|
||||
|
||||
class IIDX9thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 9th style'
|
||||
name: str = "Beatmania IIDX 9th style"
|
||||
version: int = VersionConstants.IIDX_9TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -85,7 +85,7 @@ class IIDX9thStyle(IIDXBase):
|
||||
|
||||
class IIDX10thStyle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX 10th style'
|
||||
name: str = "Beatmania IIDX 10th style"
|
||||
version: int = VersionConstants.IIDX_10TH_STYLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -94,7 +94,7 @@ class IIDX10thStyle(IIDXBase):
|
||||
|
||||
class IIDXRed(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX RED'
|
||||
name: str = "Beatmania IIDX RED"
|
||||
version: int = VersionConstants.IIDX_RED
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -103,7 +103,7 @@ class IIDXRed(IIDXBase):
|
||||
|
||||
class IIDXHappySky(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX HAPPY SKY'
|
||||
name: str = "Beatmania IIDX HAPPY SKY"
|
||||
version: int = VersionConstants.IIDX_HAPPY_SKY
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -112,7 +112,7 @@ class IIDXHappySky(IIDXBase):
|
||||
|
||||
class IIDXDistorted(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX DistorteD'
|
||||
name: str = "Beatmania IIDX DistorteD"
|
||||
version: int = VersionConstants.IIDX_DISTORTED
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -121,7 +121,7 @@ class IIDXDistorted(IIDXBase):
|
||||
|
||||
class IIDXGold(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX GOLD'
|
||||
name: str = "Beatmania IIDX GOLD"
|
||||
version: int = VersionConstants.IIDX_GOLD
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -130,7 +130,7 @@ class IIDXGold(IIDXBase):
|
||||
|
||||
class IIDXDJTroopers(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX DJ TROOPERS'
|
||||
name: str = "Beatmania IIDX DJ TROOPERS"
|
||||
version: int = VersionConstants.IIDX_DJ_TROOPERS
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -139,7 +139,7 @@ class IIDXDJTroopers(IIDXBase):
|
||||
|
||||
class IIDXEmpress(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX EMPRESS'
|
||||
name: str = "Beatmania IIDX EMPRESS"
|
||||
version: int = VersionConstants.IIDX_EMPRESS
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -148,7 +148,7 @@ class IIDXEmpress(IIDXBase):
|
||||
|
||||
class IIDXSirius(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX SIRIUS'
|
||||
name: str = "Beatmania IIDX SIRIUS"
|
||||
version: int = VersionConstants.IIDX_SIRIUS
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -157,7 +157,7 @@ class IIDXSirius(IIDXBase):
|
||||
|
||||
class IIDXResortAnthem(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX Resort Anthem'
|
||||
name: str = "Beatmania IIDX Resort Anthem"
|
||||
version: int = VersionConstants.IIDX_RESORT_ANTHEM
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
@ -166,7 +166,7 @@ class IIDXResortAnthem(IIDXBase):
|
||||
|
||||
class IIDXLincle(IIDXBase):
|
||||
|
||||
name: str = 'Beatmania IIDX Lincle'
|
||||
name: str = "Beatmania IIDX Lincle"
|
||||
version: int = VersionConstants.IIDX_LINCLE
|
||||
|
||||
def previous_version(self) -> Optional[IIDXBase]:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ from bemani.common import VersionConstants
|
||||
|
||||
class JubeatAvenue(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Avenue'
|
||||
name: str = "Jubeat Avenue"
|
||||
version: int = VersionConstants.JUBEAT_AVENUE
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
|
@ -28,9 +28,13 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
PLAY_MEDAL_FAILED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_FAILED
|
||||
PLAY_MEDAL_CLEARED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_CLEARED
|
||||
PLAY_MEDAL_NEARLY_FULL_COMBO: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO
|
||||
PLAY_MEDAL_NEARLY_FULL_COMBO: Final[
|
||||
int
|
||||
] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO
|
||||
PLAY_MEDAL_FULL_COMBO: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO
|
||||
PLAY_MEDAL_NEARLY_EXCELLENT: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT
|
||||
PLAY_MEDAL_NEARLY_EXCELLENT: Final[
|
||||
int
|
||||
] = DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT
|
||||
PLAY_MEDAL_EXCELLENT: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT
|
||||
|
||||
CHART_TYPE_BASIC: Final[int] = 0
|
||||
@ -42,7 +46,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||
super().__init__(data, config, model)
|
||||
if model.rev == 'X' or model.rev == 'Y':
|
||||
if model.rev == "X" or model.rev == "Y":
|
||||
self.omnimix = True
|
||||
else:
|
||||
self.omnimix = False
|
||||
@ -53,7 +57,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||
return self.version
|
||||
|
||||
def previous_version(self) -> Optional['JubeatBase']:
|
||||
def previous_version(self) -> Optional["JubeatBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -69,8 +73,8 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
userid - The user ID we are saving the profile for.
|
||||
profile - A dictionary that should be looked up later using get_profile.
|
||||
"""
|
||||
if 'has_old_version' in profile:
|
||||
del profile['has_old_version']
|
||||
if "has_old_version" in profile:
|
||||
del profile["has_old_version"]
|
||||
super().put_profile(userid, profile)
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
@ -78,16 +82,20 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('gametop')
|
||||
return Node.void("gametop")
|
||||
|
||||
def format_scores(self, userid: UserID, profile: Profile, scores: List[Score]) -> Node:
|
||||
def format_scores(
|
||||
self, userid: UserID, profile: Profile, scores: List[Score]
|
||||
) -> Node:
|
||||
"""
|
||||
Base handler for a score list. Given a userid, profile and a score list,
|
||||
return a Node representing a score list. Should be overridden.
|
||||
"""
|
||||
return Node.void('gametop')
|
||||
return Node.void("gametop")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -113,7 +121,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# Now try to find out if the profile is new or old
|
||||
oldversion = self.previous_version()
|
||||
oldprofile = oldversion.get_profile(userid)
|
||||
profile['has_old_version'] = oldprofile is not None
|
||||
profile["has_old_version"] = oldprofile is not None
|
||||
|
||||
# Now, return it
|
||||
return self.format_profile(userid, profile)
|
||||
@ -127,7 +135,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = 'なし'
|
||||
name = "なし"
|
||||
|
||||
# First, create and save the default profile
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -137,7 +145,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
"name": name,
|
||||
},
|
||||
)
|
||||
self.put_profile(userid, profile)
|
||||
@ -145,7 +153,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# Now, reload and format the profile, looking up the has old version flag
|
||||
oldversion = self.previous_version()
|
||||
oldprofile = oldversion.get_profile(userid)
|
||||
profile['has_old_version'] = oldprofile is not None
|
||||
profile["has_old_version"] = oldprofile is not None
|
||||
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
@ -158,7 +166,9 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
if scores is None:
|
||||
return None
|
||||
profile = self.get_profile(userid)
|
||||
@ -175,9 +185,9 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
points: int,
|
||||
medal: int,
|
||||
combo: int,
|
||||
ghost: Optional[List[int]]=None,
|
||||
stats: Optional[Dict[str, int]]=None,
|
||||
music_rate: Optional[int]=None,
|
||||
ghost: Optional[List[int]] = None,
|
||||
stats: Optional[Dict[str, int]] = None,
|
||||
music_rate: Optional[int] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -220,38 +230,38 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Replace medal with highest value
|
||||
scoredata.replace_int('medal', max(scoredata.get_int('medal'), medal))
|
||||
history.replace_int('medal', medal)
|
||||
scoredata.replace_int("medal", max(scoredata.get_int("medal"), medal))
|
||||
history.replace_int("medal", medal)
|
||||
|
||||
# Increment counters based on medal
|
||||
if medal == self.PLAY_MEDAL_CLEARED:
|
||||
scoredata.increment_int('clear_count')
|
||||
scoredata.increment_int("clear_count")
|
||||
if medal == self.PLAY_MEDAL_FULL_COMBO:
|
||||
scoredata.increment_int('full_combo_count')
|
||||
scoredata.increment_int("full_combo_count")
|
||||
if medal == self.PLAY_MEDAL_EXCELLENT:
|
||||
scoredata.increment_int('excellent_count')
|
||||
scoredata.increment_int("excellent_count")
|
||||
|
||||
# If we have a combo, replace it
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
history.replace_int("combo", combo)
|
||||
|
||||
if stats is not None:
|
||||
if raised:
|
||||
# We have stats, and there's a new high score, update the stats
|
||||
scoredata.replace_dict('stats', stats)
|
||||
history.replace_dict('stats', stats)
|
||||
scoredata.replace_dict("stats", stats)
|
||||
history.replace_dict("stats", stats)
|
||||
|
||||
if ghost is not None:
|
||||
# Update the ghost regardless, but don't bother with it in history
|
||||
scoredata.replace_int_array('ghost', len(ghost), ghost)
|
||||
scoredata.replace_int_array("ghost", len(ghost), ghost)
|
||||
|
||||
if music_rate is not None:
|
||||
if oldscore is not None:
|
||||
if music_rate > oldscore.data.get_int('music_rate'):
|
||||
scoredata.replace_int('music_rate', music_rate)
|
||||
if music_rate > oldscore.data.get_int("music_rate"):
|
||||
scoredata.replace_int("music_rate", music_rate)
|
||||
else:
|
||||
scoredata.replace_int('music_rate', music_rate)
|
||||
history.replace_int('music_rate', music_rate)
|
||||
scoredata.replace_int("music_rate", music_rate)
|
||||
history.replace_int("music_rate", music_rate)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,83 +5,78 @@ from bemani.protocol import Node
|
||||
|
||||
|
||||
class JubeatLoggerReportHandler(JubeatBase):
|
||||
|
||||
def handle_logger_report_request(self, request: Node) -> Node:
|
||||
# Handle this by returning nothing, game doesn't care
|
||||
root = Node.void('logger')
|
||||
root = Node.void("logger")
|
||||
return root
|
||||
|
||||
|
||||
class JubeatDemodataGetNewsHandler(JubeatBase):
|
||||
|
||||
def handle_demodata_get_news_request(self, request: Node) -> Node:
|
||||
demodata = Node.void('demodata')
|
||||
data = Node.void('data')
|
||||
demodata = Node.void("demodata")
|
||||
data = Node.void("data")
|
||||
demodata.add_child(data)
|
||||
|
||||
officialnews = Node.void('officialnews')
|
||||
officialnews = Node.void("officialnews")
|
||||
data.add_child(officialnews)
|
||||
officialnews.set_attribute('count', '0')
|
||||
officialnews.set_attribute("count", "0")
|
||||
|
||||
return demodata
|
||||
|
||||
|
||||
class JubeatDemodataGetHitchartHandler(JubeatBase):
|
||||
|
||||
def handle_demodata_get_hitchart_request(self, request: Node) -> Node:
|
||||
demodata = Node.void('demodata')
|
||||
data = Node.void('data')
|
||||
demodata = Node.void("demodata")
|
||||
data = Node.void("data")
|
||||
demodata.add_child(data)
|
||||
|
||||
# Not sure what this is, maybe date?
|
||||
data.add_child(Node.string('update', time.strftime("%d/%m/%Y")))
|
||||
data.add_child(Node.string("update", time.strftime("%d/%m/%Y")))
|
||||
|
||||
# No idea which songs are licensed or regular, so only return hit chart
|
||||
# for all songs on regular mode.
|
||||
hitchart_lic = Node.void('hitchart_lic')
|
||||
hitchart_lic = Node.void("hitchart_lic")
|
||||
data.add_child(hitchart_lic)
|
||||
hitchart_lic.set_attribute('count', '0')
|
||||
hitchart_lic.set_attribute("count", "0")
|
||||
|
||||
songs = self.data.local.music.get_hit_chart(self.game, self.music_version, 10)
|
||||
hitchart_org = Node.void('hitchart_org')
|
||||
hitchart_org = Node.void("hitchart_org")
|
||||
data.add_child(hitchart_org)
|
||||
hitchart_org.set_attribute('count', str(len(songs)))
|
||||
hitchart_org.set_attribute("count", str(len(songs)))
|
||||
rank = 1
|
||||
for song in songs:
|
||||
rankdata = Node.void('rankdata')
|
||||
rankdata = Node.void("rankdata")
|
||||
hitchart_org.add_child(rankdata)
|
||||
rankdata.add_child(Node.s32('music_id', song[0]))
|
||||
rankdata.add_child(Node.s16('rank', rank))
|
||||
rankdata.add_child(Node.s16('prev', rank))
|
||||
rankdata.add_child(Node.s32("music_id", song[0]))
|
||||
rankdata.add_child(Node.s16("rank", rank))
|
||||
rankdata.add_child(Node.s16("prev", rank))
|
||||
rank = rank + 1
|
||||
|
||||
return demodata
|
||||
|
||||
|
||||
class JubeatLobbyCheckHandler(JubeatBase):
|
||||
|
||||
def handle_lobby_check_request(self, request: Node) -> Node:
|
||||
root = Node.void('lobby')
|
||||
data = Node.void('data')
|
||||
root = Node.void("lobby")
|
||||
data = Node.void("data")
|
||||
root.add_child(data)
|
||||
|
||||
data.add_child(Node.s16('interval', 0))
|
||||
data.add_child(Node.s16('entry_timeout', 0))
|
||||
entrant_nr = Node.u32('entrant_nr', 0)
|
||||
entrant_nr.set_attribute('time', '0')
|
||||
data.add_child(Node.s16("interval", 0))
|
||||
data.add_child(Node.s16("entry_timeout", 0))
|
||||
entrant_nr = Node.u32("entrant_nr", 0)
|
||||
entrant_nr.set_attribute("time", "0")
|
||||
data.add_child(entrant_nr)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
class JubeatGamendRegisterHandler(JubeatBase):
|
||||
|
||||
def handle_gameend_regist_request(self, request: Node) -> Node:
|
||||
data = request.child('data')
|
||||
player = data.child('player')
|
||||
data = request.child("data")
|
||||
player = data.child("player")
|
||||
|
||||
if player is not None:
|
||||
refid = player.child_value('refid')
|
||||
refid = player.child_value("refid")
|
||||
else:
|
||||
refid = None
|
||||
|
||||
@ -99,32 +94,31 @@ class JubeatGamendRegisterHandler(JubeatBase):
|
||||
if userid is not None and newprofile is not None:
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
gameend = Node.void('gameend')
|
||||
data = Node.void('data')
|
||||
gameend = Node.void("gameend")
|
||||
data = Node.void("data")
|
||||
gameend.add_child(data)
|
||||
player = Node.void('player')
|
||||
player = Node.void("player")
|
||||
data.add_child(player)
|
||||
player.add_child(Node.s32('session_id', 1))
|
||||
player.add_child(Node.s32('end_final_session_id', 1))
|
||||
player.add_child(Node.s32("session_id", 1))
|
||||
player.add_child(Node.s32("end_final_session_id", 1))
|
||||
return gameend
|
||||
|
||||
|
||||
class JubeatGametopGetMeetingHandler(JubeatBase):
|
||||
|
||||
def handle_gametop_get_meeting_request(self, request: Node) -> Node:
|
||||
gametop = Node.void('gametop')
|
||||
data = Node.void('data')
|
||||
gametop = Node.void("gametop")
|
||||
data = Node.void("data")
|
||||
gametop.add_child(data)
|
||||
meeting = Node.void('meeting')
|
||||
meeting = Node.void("meeting")
|
||||
data.add_child(meeting)
|
||||
single = Node.void('single')
|
||||
single = Node.void("single")
|
||||
meeting.add_child(single)
|
||||
single.set_attribute('count', '0')
|
||||
tag = Node.void('tag')
|
||||
single.set_attribute("count", "0")
|
||||
tag = Node.void("tag")
|
||||
meeting.add_child(tag)
|
||||
tag.set_attribute('count', '0')
|
||||
reward = Node.void('reward')
|
||||
tag.set_attribute("count", "0")
|
||||
reward = Node.void("reward")
|
||||
data.add_child(reward)
|
||||
reward.add_child(Node.s32('total', -1))
|
||||
reward.add_child(Node.s32('point', -1))
|
||||
reward.add_child(Node.s32("total", -1))
|
||||
reward.add_child(Node.s32("point", -1))
|
||||
return gametop
|
||||
|
@ -21,462 +21,462 @@ class JubeatCourse(JubeatBase):
|
||||
# List of base courses for Saucer Fulfill+ from BemaniWiki
|
||||
return [
|
||||
{
|
||||
'id': 1,
|
||||
'name': '溢れ出した記憶、特別なあなたにありがとう。',
|
||||
'level': 1,
|
||||
'music': [
|
||||
"id": 1,
|
||||
"name": "溢れ出した記憶、特別なあなたにありがとう。",
|
||||
"level": 1,
|
||||
"music": [
|
||||
(50000241, 2),
|
||||
(10000052, 2),
|
||||
(30000042, 2),
|
||||
(50000085, 2),
|
||||
(50000144, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': 'コースモードが怖い?ばっかお前TAGがついてるだろ',
|
||||
'level': 1,
|
||||
'music': [
|
||||
"id": 2,
|
||||
"name": "コースモードが怖い?ばっかお前TAGがついてるだろ",
|
||||
"level": 1,
|
||||
"music": [
|
||||
(50000121, 1),
|
||||
(30000122, 1),
|
||||
(40000159, 1),
|
||||
(50000089, 1),
|
||||
(40000051, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [800000, 850000, 900000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'name': '満月の鐘踊り響くは虚空から成る恋の歌',
|
||||
'level': 2,
|
||||
'music': [
|
||||
"id": 3,
|
||||
"name": "満月の鐘踊り響くは虚空から成る恋の歌",
|
||||
"level": 2,
|
||||
"music": [
|
||||
(40000121, 2),
|
||||
(50000188, 2),
|
||||
(30000047, 2),
|
||||
(50000237, 2),
|
||||
(50000176, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 4,
|
||||
'name': 'スミスゼミナール 夏の陣開講記念 基本編',
|
||||
'level': 2,
|
||||
'music': [
|
||||
"id": 4,
|
||||
"name": "スミスゼミナール 夏の陣開講記念 基本編",
|
||||
"level": 2,
|
||||
"music": [
|
||||
(50000267, 1),
|
||||
(50000233, 1),
|
||||
(50000228, 1),
|
||||
(50000268, 1),
|
||||
(50000291, 1),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 5,
|
||||
'name': 'HARDモードじゃないから、絶対、大丈夫だよっ!',
|
||||
'level': 2,
|
||||
'music': [
|
||||
"id": 5,
|
||||
"name": "HARDモードじゃないから、絶対、大丈夫だよっ!",
|
||||
"level": 2,
|
||||
"music": [
|
||||
(50000144, 2),
|
||||
(50000188, 2),
|
||||
(50000070, 2),
|
||||
(50000151, 2),
|
||||
(50000152, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 6,
|
||||
'name': '星明かりの下、愛という名の日替わりランチを君と',
|
||||
'level': 3,
|
||||
'music': [
|
||||
"id": 6,
|
||||
"name": "星明かりの下、愛という名の日替わりランチを君と",
|
||||
"level": 3,
|
||||
"music": [
|
||||
(50000196, 1),
|
||||
(50000151, 2),
|
||||
(50000060, 1),
|
||||
(40000048, 2),
|
||||
(10000051, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 7,
|
||||
'name': '輝く北極星と幸せなヒーロー',
|
||||
'level': 4,
|
||||
'music': [
|
||||
"id": 7,
|
||||
"name": "輝く北極星と幸せなヒーロー",
|
||||
"level": 4,
|
||||
"music": [
|
||||
(50000079, 2),
|
||||
(20000044, 2),
|
||||
(50000109, 2),
|
||||
(10000043, 2),
|
||||
(10000042, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 8,
|
||||
'name': '花-鳥-藻-夏',
|
||||
'level': 4,
|
||||
'music': [
|
||||
"id": 8,
|
||||
"name": "花-鳥-藻-夏",
|
||||
"level": 4,
|
||||
"music": [
|
||||
(10000068, 2),
|
||||
(40000154, 2),
|
||||
(50000123, 1),
|
||||
(40000051, 2),
|
||||
(30000045, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 9,
|
||||
'name': 'TAG生誕祭2014 俺の記録を抜いてみろ!',
|
||||
'level': 4,
|
||||
'music': [
|
||||
"id": 9,
|
||||
"name": "TAG生誕祭2014 俺の記録を抜いてみろ!",
|
||||
"level": 4,
|
||||
"music": [
|
||||
(30000122, 2),
|
||||
(50000086, 2),
|
||||
(50000121, 2),
|
||||
(50000196, 2),
|
||||
(40000051, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 967252],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 0, 1],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 10,
|
||||
'name': 'さよなら、亡くした恋と蝶の舞うヒストリア',
|
||||
'level': 5,
|
||||
'music': [
|
||||
"id": 10,
|
||||
"name": "さよなら、亡くした恋と蝶の舞うヒストリア",
|
||||
"level": 5,
|
||||
"music": [
|
||||
(20000041, 2),
|
||||
(30000044, 2),
|
||||
(50000037, 2),
|
||||
(20000124, 2),
|
||||
(50000033, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 11,
|
||||
'name': 'きらきらほしふるまぼろしなぎさちゃん',
|
||||
'level': 5,
|
||||
'music': [
|
||||
"id": 11,
|
||||
"name": "きらきらほしふるまぼろしなぎさちゃん",
|
||||
"level": 5,
|
||||
"music": [
|
||||
(30000050, 2),
|
||||
(30000049, 2),
|
||||
(50000235, 2),
|
||||
(50000157, 2),
|
||||
(50000038, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [700000, 800000, 900000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 12,
|
||||
'name': 'The Memorial Third: 僕みたいに演奏してね',
|
||||
'level': 5,
|
||||
'music': [
|
||||
"id": 12,
|
||||
"name": "The Memorial Third: 僕みたいに演奏してね",
|
||||
"level": 5,
|
||||
"music": [
|
||||
(10000037, 2),
|
||||
(20000048, 1),
|
||||
(50000253, 1),
|
||||
(20000121, 2),
|
||||
(50000133, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [75, 80, 85],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 13,
|
||||
'name': 'Enjoy! 4thKAC ~ Memories of saucer ~',
|
||||
'level': 5,
|
||||
'music': [
|
||||
"id": 13,
|
||||
"name": "Enjoy! 4thKAC ~ Memories of saucer ~",
|
||||
"level": 5,
|
||||
"music": [
|
||||
(50000206, 1),
|
||||
(50000023, 1),
|
||||
(50000078, 1),
|
||||
(50000203, 1),
|
||||
(50000323, 1),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 4],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 14,
|
||||
'name': '風に吹かれるキケンなシロクマダンス',
|
||||
'level': 6,
|
||||
'music': [
|
||||
"id": 14,
|
||||
"name": "風に吹かれるキケンなシロクマダンス",
|
||||
"level": 6,
|
||||
"music": [
|
||||
(50000059, 2),
|
||||
(50000197, 2),
|
||||
(30000037, 2),
|
||||
(50000182, 2),
|
||||
(20000038, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 15,
|
||||
'name': '君主は視線で友との愛を語るめう',
|
||||
'level': 6,
|
||||
'music': [
|
||||
"id": 15,
|
||||
"name": "君主は視線で友との愛を語るめう",
|
||||
"level": 6,
|
||||
"music": [
|
||||
(40000052, 2),
|
||||
(50000152, 2),
|
||||
(50000090, 2),
|
||||
(20000040, 2),
|
||||
(50000184, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 16,
|
||||
'name': 'スミスゼミナール 夏の陣開講記念 応用編',
|
||||
'level': 6,
|
||||
'music': [
|
||||
"id": 16,
|
||||
"name": "スミスゼミナール 夏の陣開講記念 応用編",
|
||||
"level": 6,
|
||||
"music": [
|
||||
(50000233, 2),
|
||||
(50000267, 2),
|
||||
(50000268, 2),
|
||||
(50000228, 2),
|
||||
(50000291, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [750000, 850000, 900000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 17,
|
||||
'name': '天から降り注ぐ星はまるで甘いキャンディ',
|
||||
'level': 7,
|
||||
'music': [
|
||||
"id": 17,
|
||||
"name": "天から降り注ぐ星はまるで甘いキャンディ",
|
||||
"level": 7,
|
||||
"music": [
|
||||
(20000044, 2),
|
||||
(30000050, 2),
|
||||
(50000080, 2),
|
||||
(40000126, 2),
|
||||
(10000067, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 18,
|
||||
'name': 'てんとう虫が囁いている「Wow Wow…」',
|
||||
'level': 7,
|
||||
'music': [
|
||||
"id": 18,
|
||||
"name": "てんとう虫が囁いている「Wow Wow…」",
|
||||
"level": 7,
|
||||
"music": [
|
||||
(50000132, 2),
|
||||
(40000128, 2),
|
||||
(10000036, 2),
|
||||
(50000119, 2),
|
||||
(50000030, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 19,
|
||||
'name': 'HARDモードでも大丈夫だよ!絶対、大丈夫だよっ!',
|
||||
'level': 7,
|
||||
'music': [
|
||||
"id": 19,
|
||||
"name": "HARDモードでも大丈夫だよ!絶対、大丈夫だよっ!",
|
||||
"level": 7,
|
||||
"music": [
|
||||
(50000144, 2),
|
||||
(50000070, 2),
|
||||
(50000188, 2),
|
||||
(50000151, 2),
|
||||
(50000152, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 20,
|
||||
'name': 'こんなHARDモード、滅べばいい…',
|
||||
'level': 7,
|
||||
'music': [
|
||||
"id": 20,
|
||||
"name": "こんなHARDモード、滅べばいい…",
|
||||
"level": 7,
|
||||
"music": [
|
||||
(50000294, 2),
|
||||
(50000295, 2),
|
||||
(50000234, 2),
|
||||
(50000245, 2),
|
||||
(50000282, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 21,
|
||||
'name': 'Challenge! 4thKAC ~ Memories of saucer ~',
|
||||
'level': 7,
|
||||
'music': [
|
||||
"id": 21,
|
||||
"name": "Challenge! 4thKAC ~ Memories of saucer ~",
|
||||
"level": 7,
|
||||
"music": [
|
||||
(50000206, 2),
|
||||
(50000023, 2),
|
||||
(50000078, 2),
|
||||
(50000203, 2),
|
||||
(50000323, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 22,
|
||||
'name': 'サヨナラ・キングコング ~ 恋のつぼみは愛の虹へ ~',
|
||||
'level': 8,
|
||||
'music': [
|
||||
"id": 22,
|
||||
"name": "サヨナラ・キングコング ~ 恋のつぼみは愛の虹へ ~",
|
||||
"level": 8,
|
||||
"music": [
|
||||
(50000148, 2),
|
||||
(50000101, 2),
|
||||
(10000064, 2),
|
||||
(50000171, 2),
|
||||
(50000070, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 23,
|
||||
'name': '風に舞う白鳥の翼と花弁、さながら万華鏡のよう',
|
||||
'level': 8,
|
||||
'music': [
|
||||
"id": 23,
|
||||
"name": "風に舞う白鳥の翼と花弁、さながら万華鏡のよう",
|
||||
"level": 8,
|
||||
"music": [
|
||||
(30000036, 2),
|
||||
(50000122, 2),
|
||||
(10000062, 2),
|
||||
(50000199, 2),
|
||||
(40000153, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [90, 95, 98],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 24,
|
||||
'name': 'The 小さなおぼろガチョウ♪',
|
||||
'level': 8,
|
||||
'music': [
|
||||
"id": 24,
|
||||
"name": "The 小さなおぼろガチョウ♪",
|
||||
"level": 8,
|
||||
"music": [
|
||||
(50000049, 2),
|
||||
(50000071, 2),
|
||||
(10000041, 2),
|
||||
(50000031, 2),
|
||||
(40000129, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [970000, 980000, 990000],
|
||||
self.COURSE_REQUIREMENT_FULL_COMBO: [2, 3, 4],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 25,
|
||||
'name': 'TAG生誕祭2014 俺の記録を抜いてみろ!~ HARD編 ~',
|
||||
'level': 8,
|
||||
'music': [
|
||||
"id": 25,
|
||||
"name": "TAG生誕祭2014 俺の記録を抜いてみろ!~ HARD編 ~",
|
||||
"level": 8,
|
||||
"music": [
|
||||
(50000089, 2),
|
||||
(50000083, 2),
|
||||
(50000210, 2),
|
||||
(50000030, 2),
|
||||
(40000159, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [800000, 900000, 931463],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 26,
|
||||
'name': '凍る世界で見る鳳凰の火の花',
|
||||
'level': 9,
|
||||
'music': [
|
||||
"id": 26,
|
||||
"name": "凍る世界で見る鳳凰の火の花",
|
||||
"level": 9,
|
||||
"music": [
|
||||
(30000043, 2),
|
||||
(10000039, 2),
|
||||
(20000048, 2),
|
||||
(50000096, 2),
|
||||
(20000038, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 27,
|
||||
'name': '真実の桜が乱れしとき、キルト纏いし君は修羅となる',
|
||||
'level': 9,
|
||||
'music': [
|
||||
"id": 27,
|
||||
"name": "真実の桜が乱れしとき、キルト纏いし君は修羅となる",
|
||||
"level": 9,
|
||||
"music": [
|
||||
(50000113, 2),
|
||||
(50000184, 2),
|
||||
(50000177, 2),
|
||||
(30000124, 2),
|
||||
(50000078, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 28,
|
||||
'name': 'THE FINAL01 ~ 雷光に月、乙女に花散る祝福を ~',
|
||||
'level': 10,
|
||||
'music': [
|
||||
"id": 28,
|
||||
"name": "THE FINAL01 ~ 雷光に月、乙女に花散る祝福を ~",
|
||||
"level": 10,
|
||||
"music": [
|
||||
(10000038, 2),
|
||||
(20000051, 2),
|
||||
(30000048, 2),
|
||||
(40000060, 2),
|
||||
(50000023, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 29,
|
||||
'name': 'The Memorial Third: assimilated all into Nature',
|
||||
'level': 10,
|
||||
'music': [
|
||||
"id": 29,
|
||||
"name": "The Memorial Third: assimilated all into Nature",
|
||||
"level": 10,
|
||||
"music": [
|
||||
(50000135, 2),
|
||||
(50000029, 2),
|
||||
(40000047, 2),
|
||||
(40000046, 2),
|
||||
(50000253, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
{
|
||||
'id': 30,
|
||||
'name': '4thKAC ~ Memories of saucer ~',
|
||||
'level': 10,
|
||||
'music': [
|
||||
"id": 30,
|
||||
"name": "4thKAC ~ Memories of saucer ~",
|
||||
"level": 10,
|
||||
"music": [
|
||||
(50000206, 2),
|
||||
(50000023, 2),
|
||||
(50000078, 2),
|
||||
(50000203, 2),
|
||||
(50000323, 2),
|
||||
],
|
||||
'requirements': {
|
||||
"requirements": {
|
||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||
},
|
||||
},
|
||||
@ -490,40 +490,40 @@ class JubeatCourse(JubeatBase):
|
||||
scores: List[int],
|
||||
) -> None:
|
||||
if len(scores) != 5:
|
||||
raise Exception('Invalid course scores list!')
|
||||
raise Exception("Invalid course scores list!")
|
||||
if rating not in [
|
||||
self.COURSE_RATING_FAILED,
|
||||
self.COURSE_RATING_BRONZE,
|
||||
self.COURSE_RATING_SILVER,
|
||||
self.COURSE_RATING_GOLD,
|
||||
]:
|
||||
raise Exception('Invalid course rating!')
|
||||
raise Exception("Invalid course rating!")
|
||||
|
||||
# Figure out if we should update the rating/scores or not
|
||||
oldcourse = self.data.local.game.get_achievement(
|
||||
self.game,
|
||||
userid,
|
||||
courseid,
|
||||
'course',
|
||||
"course",
|
||||
)
|
||||
|
||||
if oldcourse is not None:
|
||||
# Update the rating if the user did better
|
||||
rating = max(rating, oldcourse.get_int('rating'))
|
||||
rating = max(rating, oldcourse.get_int("rating"))
|
||||
|
||||
# Update the scores if the total score was better
|
||||
if sum(scores) < sum(oldcourse.get_int_array('scores', 5)):
|
||||
scores = oldcourse.get_int_array('scores', 5)
|
||||
if sum(scores) < sum(oldcourse.get_int_array("scores", 5)):
|
||||
scores = oldcourse.get_int_array("scores", 5)
|
||||
|
||||
# Save it as an achievement
|
||||
self.data.local.game.put_achievement(
|
||||
self.game,
|
||||
userid,
|
||||
courseid,
|
||||
'course',
|
||||
"course",
|
||||
{
|
||||
'rating': rating,
|
||||
'scores': scores,
|
||||
"rating": rating,
|
||||
"scores": scores,
|
||||
},
|
||||
)
|
||||
|
||||
@ -534,9 +534,9 @@ class JubeatCourse(JubeatBase):
|
||||
courses = {}
|
||||
achievements = self.data.local.game.get_achievements(self.game, userid)
|
||||
for achievement in achievements:
|
||||
if achievement.type == 'course':
|
||||
if achievement.type == "course":
|
||||
courses[achievement.id] = {
|
||||
'rating': achievement.data.get_int('rating'),
|
||||
'scores': achievement.data.get_int_array('scores', 5),
|
||||
"rating": achievement.data.get_int("rating"),
|
||||
"scores": achievement.data.get_int_array("scores", 5),
|
||||
}
|
||||
return courses
|
||||
|
@ -42,29 +42,35 @@ class JubeatFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['H44', 'I44', 'J44', 'K44', 'L44']:
|
||||
for gamecode in ["H44", "I44", "J44", "K44", "L44"]:
|
||||
Base.register(gamecode, JubeatFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
if model.gamecode == 'H44':
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
if model.gamecode == "H44":
|
||||
return Jubeat(data, config, model)
|
||||
if model.gamecode == 'I44':
|
||||
if model.gamecode == "I44":
|
||||
if model.version >= 2010031800:
|
||||
return JubeatRipplesAppend(data, config, model)
|
||||
else:
|
||||
return JubeatRipples(data, config, model)
|
||||
if model.gamecode == 'J44':
|
||||
if model.gamecode == "J44":
|
||||
if model.version >= 2011032300:
|
||||
return JubeatKnitAppend(data, config, model)
|
||||
else:
|
||||
return JubeatKnit(data, config, model)
|
||||
if model.gamecode == 'K44':
|
||||
if model.gamecode == "K44":
|
||||
if model.version >= 2012031400:
|
||||
return JubeatCopiousAppend(data, config, model)
|
||||
else:
|
||||
return JubeatCopious(data, config, model)
|
||||
if model.gamecode == 'L44':
|
||||
if model.gamecode == "L44":
|
||||
if model.version <= 2014022400:
|
||||
return JubeatSaucer(data, config, model)
|
||||
if model.version >= 2014030300 and model.version < 2015022000:
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,13 @@ from bemani.common import VersionConstants
|
||||
|
||||
class Jubeat(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat'
|
||||
name: str = "Jubeat"
|
||||
version: int = VersionConstants.JUBEAT
|
||||
|
||||
|
||||
class JubeatRipples(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Ripples'
|
||||
name: str = "Jubeat Ripples"
|
||||
version: int = VersionConstants.JUBEAT_RIPPLES
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
@ -22,7 +22,7 @@ class JubeatRipples(JubeatBase):
|
||||
|
||||
class JubeatRipplesAppend(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Ripples Append'
|
||||
name: str = "Jubeat Ripples Append"
|
||||
version: int = VersionConstants.JUBEAT_RIPPLES_APPEND
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
@ -31,7 +31,7 @@ class JubeatRipplesAppend(JubeatBase):
|
||||
|
||||
class JubeatKnit(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Knit'
|
||||
name: str = "Jubeat Knit"
|
||||
version: int = VersionConstants.JUBEAT_KNIT
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
@ -40,7 +40,7 @@ class JubeatKnit(JubeatBase):
|
||||
|
||||
class JubeatKnitAppend(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Knit Append'
|
||||
name: str = "Jubeat Knit Append"
|
||||
version: int = VersionConstants.JUBEAT_KNIT_APPEND
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
@ -49,7 +49,7 @@ class JubeatKnitAppend(JubeatBase):
|
||||
|
||||
class JubeatCopious(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Copious'
|
||||
name: str = "Jubeat Copious"
|
||||
version: int = VersionConstants.JUBEAT_COPIOUS
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
@ -58,7 +58,7 @@ class JubeatCopious(JubeatBase):
|
||||
|
||||
class JubeatCopiousAppend(JubeatBase):
|
||||
|
||||
name: str = 'Jubeat Copious Append'
|
||||
name: str = "Jubeat Copious Append"
|
||||
version: int = VersionConstants.JUBEAT_COPIOUS_APPEND
|
||||
|
||||
def previous_version(self) -> Optional[JubeatBase]:
|
||||
|
@ -13,7 +13,7 @@ class MetalGearArcadeBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
game: GameConstants = GameConstants.MGA
|
||||
|
||||
def previous_version(self) -> Optional['MetalGearArcadeBase']:
|
||||
def previous_version(self) -> Optional["MetalGearArcadeBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
|
@ -14,13 +14,19 @@ class MetalGearArcadeFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['I36']:
|
||||
for gamecode in ["I36"]:
|
||||
Base.register(gamecode, MetalGearArcadeFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
|
||||
if model.gamecode == 'I36':
|
||||
if model.gamecode == "I36":
|
||||
return MetalGearArcade(data, config, model)
|
||||
|
||||
# Unknown game version
|
||||
|
@ -19,36 +19,36 @@ class MetalGearArcade(
|
||||
|
||||
def __update_shop_name(self, profiledata: bytes) -> None:
|
||||
# Figure out the profile type
|
||||
csvs = profiledata.split(b',')
|
||||
csvs = profiledata.split(b",")
|
||||
if len(csvs) < 2:
|
||||
# Not long enough to care about
|
||||
return
|
||||
datatype = csvs[1].decode('ascii')
|
||||
if datatype != 'PLAYDATA':
|
||||
datatype = csvs[1].decode("ascii")
|
||||
if datatype != "PLAYDATA":
|
||||
# Not the right profile type requested
|
||||
return
|
||||
|
||||
# Grab the shop name
|
||||
try:
|
||||
shopname = csvs[30].decode('shift-jis')
|
||||
shopname = csvs[30].decode("shift-jis")
|
||||
except Exception:
|
||||
return
|
||||
self.update_machine_name(shopname)
|
||||
|
||||
def handle_system_getmaster_request(self, request: Node) -> Node:
|
||||
# See if we can grab the request
|
||||
data = request.child('data')
|
||||
data = request.child("data")
|
||||
if not data:
|
||||
root = Node.void('system')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root = Node.void("system")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
return root
|
||||
|
||||
# Figure out what type of messsage this is
|
||||
reqtype = data.child_value('datatype')
|
||||
reqkey = data.child_value('datakey')
|
||||
reqtype = data.child_value("datatype")
|
||||
reqkey = data.child_value("datakey")
|
||||
|
||||
# System message
|
||||
root = Node.void('system')
|
||||
root = Node.void("system")
|
||||
|
||||
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
||||
# Generate system message
|
||||
@ -56,23 +56,35 @@ class MetalGearArcade(
|
||||
settings2_str = "1,1,1,1,1,1,1,1,1,1,1,1,1,1"
|
||||
|
||||
# Send it to the client, making sure to inform the client that it was valid.
|
||||
root.add_child(Node.string('strdata1', base64.b64encode(settings1_str.encode('ascii')).decode('ascii')))
|
||||
root.add_child(Node.string('strdata2', base64.b64encode(settings2_str.encode('ascii')).decode('ascii')))
|
||||
root.add_child(Node.u64('updatedate', Time.now() * 1000))
|
||||
root.add_child(Node.s32('result', 1))
|
||||
root.add_child(
|
||||
Node.string(
|
||||
"strdata1",
|
||||
base64.b64encode(settings1_str.encode("ascii")).decode("ascii"),
|
||||
)
|
||||
)
|
||||
root.add_child(
|
||||
Node.string(
|
||||
"strdata2",
|
||||
base64.b64encode(settings2_str.encode("ascii")).decode("ascii"),
|
||||
)
|
||||
)
|
||||
root.add_child(Node.u64("updatedate", Time.now() * 1000))
|
||||
root.add_child(Node.s32("result", 1))
|
||||
else:
|
||||
# Unknown message.
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root.add_child(Node.s32("result", 0))
|
||||
|
||||
return root
|
||||
|
||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||
# Look up user by refid
|
||||
refid = request.child_value('data/eaid')
|
||||
refid = request.child_value("data/eaid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(
|
||||
Node.s32("result", 1)
|
||||
) # Unclear if this is the right thing to do here.
|
||||
return root
|
||||
|
||||
# Extract new profile info from old profile
|
||||
@ -87,14 +99,14 @@ class MetalGearArcade(
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
# Return success!
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
return root
|
||||
|
||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||
# Look up user by refid
|
||||
refid = request.child_value('data/eaid')
|
||||
profiletypes = request.child_value('data/recv_csv').split(',')
|
||||
refid = request.child_value("data/eaid")
|
||||
profiletypes = request.child_value("data/recv_csv").split(",")
|
||||
profile = None
|
||||
userid = None
|
||||
if refid is not None:
|
||||
@ -104,58 +116,66 @@ class MetalGearArcade(
|
||||
if profile is not None:
|
||||
return self.format_profile(userid, profiletypes, profile)
|
||||
else:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(
|
||||
Node.s32("result", 1)
|
||||
) # Unclear if this is the right thing to do here.
|
||||
return root
|
||||
|
||||
def format_profile(self, userid: UserID, profiletypes: List[str], profile: Profile) -> Node:
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s32('result', 0))
|
||||
player = Node.void('player')
|
||||
def format_profile(
|
||||
self, userid: UserID, profiletypes: List[str], profile: Profile
|
||||
) -> Node:
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
player = Node.void("player")
|
||||
root.add_child(player)
|
||||
records = 0
|
||||
record = Node.void('record')
|
||||
record = Node.void("record")
|
||||
player.add_child(record)
|
||||
|
||||
for profiletype in profiletypes:
|
||||
if profiletype == "3fffffffff":
|
||||
continue
|
||||
for j in range(len(profile['strdatas'])):
|
||||
strdata = profile['strdatas'][j]
|
||||
bindata = profile['bindatas'][j]
|
||||
for j in range(len(profile["strdatas"])):
|
||||
strdata = profile["strdatas"][j]
|
||||
bindata = profile["bindatas"][j]
|
||||
|
||||
# Figure out the profile type
|
||||
csvs = strdata.split(b',')
|
||||
csvs = strdata.split(b",")
|
||||
if len(csvs) < 2:
|
||||
# Not long enough to care about
|
||||
continue
|
||||
datatype = csvs[1].decode('ascii')
|
||||
datatype = csvs[1].decode("ascii")
|
||||
if datatype != profiletype:
|
||||
# Not the right profile type requested
|
||||
continue
|
||||
|
||||
# This is a valid profile node for this type, lets return only the profile values
|
||||
strdata = b','.join(csvs[2:])
|
||||
d = Node.string('d', base64.b64encode(strdata).decode('ascii'))
|
||||
strdata = b",".join(csvs[2:])
|
||||
d = Node.string("d", base64.b64encode(strdata).decode("ascii"))
|
||||
record.add_child(d)
|
||||
d.add_child(Node.string('bin1', base64.b64encode(bindata).decode('ascii')))
|
||||
d.add_child(
|
||||
Node.string("bin1", base64.b64encode(bindata).decode("ascii"))
|
||||
)
|
||||
|
||||
# Remember that we had this record
|
||||
records = records + 1
|
||||
|
||||
player.add_child(Node.u32('record_num', records))
|
||||
player.add_child(Node.u32("record_num", records))
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile, is_new: bool
|
||||
) -> Profile:
|
||||
# Profile save request, data values are base64 encoded.
|
||||
# d is a CSV, and bin1 is binary data.
|
||||
newprofile = oldprofile.clone()
|
||||
strdatas: List[bytes] = []
|
||||
bindatas: List[bytes] = []
|
||||
|
||||
record = request.child('data/record')
|
||||
record = request.child("data/record")
|
||||
for node in record.children:
|
||||
if node.name != 'd':
|
||||
if node.name != "d":
|
||||
continue
|
||||
|
||||
profile = base64.b64decode(node.value)
|
||||
@ -165,10 +185,10 @@ class MetalGearArcade(
|
||||
if is_new:
|
||||
self.__update_shop_name(profile)
|
||||
strdatas.append(profile)
|
||||
bindatas.append(base64.b64decode(node.child_value('bin1')))
|
||||
bindatas.append(base64.b64decode(node.child_value("bin1")))
|
||||
|
||||
newprofile['strdatas'] = strdatas
|
||||
newprofile['bindatas'] = bindatas
|
||||
newprofile["strdatas"] = strdatas
|
||||
newprofile["bindatas"] = bindatas
|
||||
|
||||
# Keep track of play statistics across all versions
|
||||
self.update_play_statistics(userid)
|
||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
||||
from bemani.common import Profile, ValidatedDict, GameConstants, DBConstants, Parallel, Model
|
||||
from bemani.common import (
|
||||
Profile,
|
||||
ValidatedDict,
|
||||
GameConstants,
|
||||
DBConstants,
|
||||
Parallel,
|
||||
Model,
|
||||
)
|
||||
from bemani.data import UserID, Config, Data
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -36,7 +43,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||
super().__init__(data, config, model)
|
||||
if model.rev == 'X':
|
||||
if model.rev == "X":
|
||||
self.omnimix = True
|
||||
else:
|
||||
self.omnimix = False
|
||||
@ -47,7 +54,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||
return self.version
|
||||
|
||||
def previous_version(self) -> Optional['MusecaBase']:
|
||||
def previous_version(self) -> Optional["MusecaBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -56,19 +63,19 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
def game_to_db_clear_type(self, clear_type: int) -> int:
|
||||
# Given a game clear type, return the canonical database identifier.
|
||||
raise Exception('Implement in subclass!')
|
||||
raise Exception("Implement in subclass!")
|
||||
|
||||
def db_to_game_clear_type(self, clear_type: int) -> int:
|
||||
# Given a database clear type, return the game's identifier.
|
||||
raise Exception('Implement in subclass!')
|
||||
raise Exception("Implement in subclass!")
|
||||
|
||||
def game_to_db_grade(self, grade: int) -> int:
|
||||
# Given a game grade, return the canonical database identifier.
|
||||
raise Exception('Implement in subclass!')
|
||||
raise Exception("Implement in subclass!")
|
||||
|
||||
def db_to_game_grade(self, grade: int) -> int:
|
||||
# Given a database grade, return the game's identifier.
|
||||
raise Exception('Implement in subclass!')
|
||||
raise Exception("Implement in subclass!")
|
||||
|
||||
def get_profile_by_refid(self, refid: Optional[str]) -> Optional[Node]:
|
||||
"""
|
||||
@ -88,7 +95,9 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# Now, return it
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
def new_profile_by_refid(self, refid: Optional[str], name: Optional[str], locid: Optional[int]) -> Node:
|
||||
def new_profile_by_refid(
|
||||
self, refid: Optional[str], name: Optional[str], locid: Optional[int]
|
||||
) -> Node:
|
||||
"""
|
||||
Given a RefID and an optional name, create a profile and then return
|
||||
a formatted profile node. Similar rationale to get_profile_by_refid.
|
||||
@ -97,7 +106,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = 'NONAME'
|
||||
name = "NONAME"
|
||||
|
||||
# First, create and save the default profile
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -107,8 +116,8 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
'loc': locid,
|
||||
"name": name,
|
||||
"loc": locid,
|
||||
},
|
||||
)
|
||||
self.put_profile(userid, profile)
|
||||
@ -119,9 +128,11 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -142,16 +153,18 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
},
|
||||
}
|
||||
"""
|
||||
all_attempts, remote_attempts = Parallel.execute([
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
)
|
||||
])
|
||||
all_attempts, remote_attempts = Parallel.execute(
|
||||
[
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.music_version,
|
||||
),
|
||||
]
|
||||
)
|
||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||
for (_, attempt) in all_attempts:
|
||||
# Terrible temporary structure is terrible.
|
||||
@ -159,19 +172,26 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
attempts[attempt.id] = {}
|
||||
if attempt.chart not in attempts[attempt.id]:
|
||||
attempts[attempt.id][attempt.chart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
}
|
||||
|
||||
# We saw an attempt, keep the total attempts in sync.
|
||||
attempts[attempt.id][attempt.chart]['total'] = attempts[attempt.id][attempt.chart]['total'] + 1
|
||||
attempts[attempt.id][attempt.chart]["total"] = (
|
||||
attempts[attempt.id][attempt.chart]["total"] + 1
|
||||
)
|
||||
|
||||
if attempt.data.get_int('clear_type', self.CLEAR_TYPE_FAILED) != self.CLEAR_TYPE_FAILED:
|
||||
if (
|
||||
attempt.data.get_int("clear_type", self.CLEAR_TYPE_FAILED)
|
||||
!= self.CLEAR_TYPE_FAILED
|
||||
):
|
||||
# This attempt was a failure, so don't count it against clears of full combos
|
||||
continue
|
||||
|
||||
# It was at least a clear
|
||||
attempts[attempt.id][attempt.chart]['clears'] = attempts[attempt.id][attempt.chart]['clears'] + 1
|
||||
attempts[attempt.id][attempt.chart]["clears"] = (
|
||||
attempts[attempt.id][attempt.chart]["clears"] + 1
|
||||
)
|
||||
|
||||
# Merge in remote attempts
|
||||
for songid in remote_attempts:
|
||||
@ -181,12 +201,16 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
for songchart in remote_attempts[songid]:
|
||||
if songchart not in attempts[songid]:
|
||||
attempts[songid][songchart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
}
|
||||
|
||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
||||
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["plays"]
|
||||
attempts[songid][songchart]["clears"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["clears"]
|
||||
|
||||
return attempts
|
||||
|
||||
@ -199,7 +223,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
clear_type: int,
|
||||
grade: int,
|
||||
combo: int,
|
||||
stats: Optional[Dict[str, int]]=None,
|
||||
stats: Optional[Dict[str, int]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -256,21 +280,23 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Replace grade and clear type
|
||||
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
|
||||
history.replace_int('clear_type', clear_type)
|
||||
scoredata.replace_int('grade', max(scoredata.get_int('grade'), grade))
|
||||
history.replace_int('grade', grade)
|
||||
scoredata.replace_int(
|
||||
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||
)
|
||||
history.replace_int("clear_type", clear_type)
|
||||
scoredata.replace_int("grade", max(scoredata.get_int("grade"), grade))
|
||||
history.replace_int("grade", grade)
|
||||
|
||||
# If we have a combo, replace it
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
history.replace_int("combo", combo)
|
||||
|
||||
# If we have play stats, replace it
|
||||
if stats is not None:
|
||||
if raised:
|
||||
# We have stats, and there's a new high score, update the stats
|
||||
scoredata.replace_dict('stats', stats)
|
||||
history.replace_dict('stats', stats)
|
||||
scoredata.replace_dict("stats", stats)
|
||||
history.replace_dict("stats", stats)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
|
@ -4,138 +4,136 @@ from bemani.protocol import Node
|
||||
|
||||
|
||||
class MusecaGameShopHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_shop_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.child_value('shopname'))
|
||||
self.update_machine_name(request.child_value("shopname"))
|
||||
|
||||
# Respond with number of milliseconds until next request
|
||||
game = Node.void('game_3')
|
||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
||||
game = Node.void("game_3")
|
||||
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||
return game
|
||||
|
||||
|
||||
class MusecaGameHiscoreHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_hiscore_request(self, request: Node) -> Node:
|
||||
# Grab location for local scores
|
||||
locid = ID.parse_machine_id(request.child_value('locid'))
|
||||
locid = ID.parse_machine_id(request.child_value("locid"))
|
||||
|
||||
# Start the response packet
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
|
||||
# First, grab hit chart
|
||||
playcounts = self.data.local.music.get_hit_chart(self.game, self.version, 1024)
|
||||
|
||||
hitchart = Node.void('hitchart')
|
||||
hitchart = Node.void("hitchart")
|
||||
game.add_child(hitchart)
|
||||
for (songid, count) in playcounts:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
hitchart.add_child(info)
|
||||
info.add_child(Node.u32('id', songid))
|
||||
info.add_child(Node.u32('cnt', count))
|
||||
info.add_child(Node.u32("id", songid))
|
||||
info.add_child(Node.u32("cnt", count))
|
||||
|
||||
# Now, grab user records
|
||||
records = self.data.remote.music.get_all_records(self.game, self.version)
|
||||
users = {
|
||||
uid: prof for (uid, prof) in
|
||||
self.get_any_profiles([r[0] for r in records])
|
||||
uid: prof for (uid, prof) in self.get_any_profiles([r[0] for r in records])
|
||||
}
|
||||
|
||||
hiscore_allover = Node.void('hiscore_allover')
|
||||
hiscore_allover = Node.void("hiscore_allover")
|
||||
game.add_child(hiscore_allover)
|
||||
|
||||
# Output records
|
||||
for (userid, score) in records:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
|
||||
if userid not in users:
|
||||
raise Exception('Logic error, could not find profile for user!')
|
||||
raise Exception("Logic error, could not find profile for user!")
|
||||
profile = users[userid]
|
||||
|
||||
info.add_child(Node.u32('id', score.id))
|
||||
info.add_child(Node.u32('type', score.chart))
|
||||
info.add_child(Node.string('name', profile.get_str('name')))
|
||||
info.add_child(Node.string('seq', ID.format_extid(profile.extid)))
|
||||
info.add_child(Node.u32('score', score.points))
|
||||
info.add_child(Node.u32("id", score.id))
|
||||
info.add_child(Node.u32("type", score.chart))
|
||||
info.add_child(Node.string("name", profile.get_str("name")))
|
||||
info.add_child(Node.string("seq", ID.format_extid(profile.extid)))
|
||||
info.add_child(Node.u32("score", score.points))
|
||||
|
||||
# Add to global scores
|
||||
hiscore_allover.add_child(info)
|
||||
|
||||
# Now, grab local records
|
||||
area_users = [
|
||||
uid for (uid, prof) in self.data.local.user.get_all_profiles(self.game, self.version)
|
||||
if prof.get_int('loc', -1) == locid
|
||||
]
|
||||
records = self.data.local.music.get_all_records(self.game, self.version, userlist=area_users)
|
||||
missing_players = [
|
||||
uid for (uid, _) in records
|
||||
if uid not in users
|
||||
uid
|
||||
for (uid, prof) in self.data.local.user.get_all_profiles(
|
||||
self.game, self.version
|
||||
)
|
||||
if prof.get_int("loc", -1) == locid
|
||||
]
|
||||
records = self.data.local.music.get_all_records(
|
||||
self.game, self.version, userlist=area_users
|
||||
)
|
||||
missing_players = [uid for (uid, _) in records if uid not in users]
|
||||
for (uid, prof) in self.get_any_profiles(missing_players):
|
||||
users[uid] = prof
|
||||
|
||||
hiscore_location = Node.void('hiscore_location')
|
||||
hiscore_location = Node.void("hiscore_location")
|
||||
game.add_child(hiscore_location)
|
||||
|
||||
# Output records
|
||||
for (userid, score) in records:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
|
||||
if userid not in users:
|
||||
raise Exception('Logic error, could not find profile for user!')
|
||||
raise Exception("Logic error, could not find profile for user!")
|
||||
profile = users[userid]
|
||||
|
||||
info.add_child(Node.u32('id', score.id))
|
||||
info.add_child(Node.u32('type', score.chart))
|
||||
info.add_child(Node.string('name', profile.get_str('name')))
|
||||
info.add_child(Node.string('seq', ID.format_extid(profile.extid)))
|
||||
info.add_child(Node.u32('score', score.points))
|
||||
info.add_child(Node.u32("id", score.id))
|
||||
info.add_child(Node.u32("type", score.chart))
|
||||
info.add_child(Node.string("name", profile.get_str("name")))
|
||||
info.add_child(Node.string("seq", ID.format_extid(profile.extid)))
|
||||
info.add_child(Node.u32("score", score.points))
|
||||
|
||||
# Add to global scores
|
||||
hiscore_location.add_child(info)
|
||||
|
||||
# Now, grab clear rates
|
||||
clear_rate = Node.void('clear_rate')
|
||||
clear_rate = Node.void("clear_rate")
|
||||
game.add_child(clear_rate)
|
||||
|
||||
clears = self.get_clear_rates()
|
||||
for songid in clears:
|
||||
for chart in clears[songid]:
|
||||
if clears[songid][chart]['total'] > 0:
|
||||
rate = float(clears[songid][chart]['clears']) / float(clears[songid][chart]['total'])
|
||||
dnode = Node.void('d')
|
||||
if clears[songid][chart]["total"] > 0:
|
||||
rate = float(clears[songid][chart]["clears"]) / float(
|
||||
clears[songid][chart]["total"]
|
||||
)
|
||||
dnode = Node.void("d")
|
||||
clear_rate.add_child(dnode)
|
||||
dnode.add_child(Node.u32('id', songid))
|
||||
dnode.add_child(Node.u32('type', chart))
|
||||
dnode.add_child(Node.s16('cr', int(rate * 10000)))
|
||||
dnode.add_child(Node.u32("id", songid))
|
||||
dnode.add_child(Node.u32("type", chart))
|
||||
dnode.add_child(Node.s16("cr", int(rate * 10000)))
|
||||
|
||||
return game
|
||||
|
||||
|
||||
class MusecaGameFrozenHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_frozen_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game.add_child(Node.u8('result', 0))
|
||||
game = Node.void("game_3")
|
||||
game.add_child(Node.u8("result", 0))
|
||||
return game
|
||||
|
||||
|
||||
class MusecaGameNewHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_new_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
name = request.child_value('name')
|
||||
loc = ID.parse_machine_id(request.child_value('locid'))
|
||||
refid = request.child_value("refid")
|
||||
name = request.child_value("name")
|
||||
loc = ID.parse_machine_id(request.child_value("locid"))
|
||||
self.new_profile_by_refid(refid, name, loc)
|
||||
|
||||
root = Node.void('game_3')
|
||||
root = Node.void("game_3")
|
||||
return root
|
||||
|
||||
|
||||
class MusecaGameSaveMusicHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -143,19 +141,19 @@ class MusecaGameSaveMusicHandler(MusecaBase):
|
||||
userid = None
|
||||
|
||||
# Doesn't matter if userid is None here, that's an anonymous score
|
||||
musicid = request.child_value('music_id')
|
||||
chart = request.child_value('music_type')
|
||||
points = request.child_value('score')
|
||||
combo = request.child_value('max_chain')
|
||||
clear_type = self.game_to_db_clear_type(request.child_value('clear_type'))
|
||||
grade = self.game_to_db_grade(request.child_value('score_grade'))
|
||||
musicid = request.child_value("music_id")
|
||||
chart = request.child_value("music_type")
|
||||
points = request.child_value("score")
|
||||
combo = request.child_value("max_chain")
|
||||
clear_type = self.game_to_db_clear_type(request.child_value("clear_type"))
|
||||
grade = self.game_to_db_grade(request.child_value("score_grade"))
|
||||
stats = {
|
||||
'btn_rate': request.child_value('btn_rate'),
|
||||
'long_rate': request.child_value('long_rate'),
|
||||
'vol_rate': request.child_value('vol_rate'),
|
||||
'critical': request.child_value('critical'),
|
||||
'near': request.child_value('near'),
|
||||
'error': request.child_value('error'),
|
||||
"btn_rate": request.child_value("btn_rate"),
|
||||
"long_rate": request.child_value("long_rate"),
|
||||
"vol_rate": request.child_value("vol_rate"),
|
||||
"critical": request.child_value("critical"),
|
||||
"near": request.child_value("near"),
|
||||
"error": request.child_value("error"),
|
||||
}
|
||||
|
||||
# Save the score
|
||||
@ -171,19 +169,17 @@ class MusecaGameSaveMusicHandler(MusecaBase):
|
||||
)
|
||||
|
||||
# Return a blank response
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
|
||||
class MusecaGamePlayEndHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_play_e_request(self, request: Node) -> Node:
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
|
||||
class MusecaGameSaveHandler(MusecaBase):
|
||||
|
||||
def handle_game_3_save_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -199,4 +195,4 @@ class MusecaGameSaveHandler(MusecaBase):
|
||||
if userid is not None and newprofile is not None:
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
@ -16,12 +16,17 @@ class MusecaFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['PIX']:
|
||||
for gamecode in ["PIX"]:
|
||||
Base.register(gamecode, MusecaFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date <= 2016072600:
|
||||
return VersionConstants.MUSECA
|
||||
@ -29,7 +34,7 @@ class MusecaFactory(Factory):
|
||||
return VersionConstants.MUSECA_1_PLUS
|
||||
return None
|
||||
|
||||
if model.gamecode == 'PIX':
|
||||
if model.gamecode == "PIX":
|
||||
version = version_from_date(model.version)
|
||||
if version == VersionConstants.MUSECA:
|
||||
return Museca1(data, config, model)
|
||||
|
@ -59,12 +59,12 @@ class Museca1(
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -109,34 +109,37 @@ class Museca1(
|
||||
}[grade]
|
||||
|
||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
limited = Node.void('music_limited')
|
||||
game = Node.void("game_3")
|
||||
limited = Node.void("music_limited")
|
||||
game.add_child(limited)
|
||||
|
||||
# Song unlock config
|
||||
game_config = self.get_game_config()
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids = set()
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||
for song in songs:
|
||||
if song.data.get_int('limited') in (self.GAME_LIMITED_LOCKED, self.GAME_LIMITED_UNLOCKABLE):
|
||||
if song.data.get_int("limited") in (
|
||||
self.GAME_LIMITED_LOCKED,
|
||||
self.GAME_LIMITED_UNLOCKABLE,
|
||||
):
|
||||
ids.add((song.id, song.chart))
|
||||
|
||||
for (songid, chart) in ids:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
limited.add_child(info)
|
||||
info.add_child(Node.s32('music_id', songid))
|
||||
info.add_child(Node.u8('music_type', chart))
|
||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
||||
info.add_child(Node.s32("music_id", songid))
|
||||
info.add_child(Node.u8("music_type", chart))
|
||||
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||
|
||||
# Event config
|
||||
event = Node.void('event')
|
||||
event = Node.void("event")
|
||||
game.add_child(event)
|
||||
|
||||
def enable_event(eid: int) -> None:
|
||||
evt = Node.void('info')
|
||||
evt = Node.void("info")
|
||||
event.add_child(evt)
|
||||
evt.add_child(Node.u32('event_id', eid))
|
||||
evt.add_child(Node.u32("event_id", eid))
|
||||
|
||||
# Allow PASELI light start
|
||||
enable_event(83)
|
||||
@ -153,21 +156,21 @@ class Museca1(
|
||||
return game
|
||||
|
||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_load_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
root = self.get_profile_by_refid(refid)
|
||||
if root is not None:
|
||||
return root
|
||||
|
||||
# No data succession, there's nothing older than this!
|
||||
root = Node.void('game_3')
|
||||
root.add_child(Node.u8('result', 1))
|
||||
root = Node.void("game_3")
|
||||
root.add_child(Node.u8("result", 1))
|
||||
return root
|
||||
|
||||
def handle_game_3_load_m_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('dataid')
|
||||
refid = request.child_value("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -175,154 +178,186 @@ class Museca1(
|
||||
userid = None
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
else:
|
||||
scores = []
|
||||
|
||||
# Output to the game
|
||||
game = Node.void('game_3')
|
||||
new = Node.void('new')
|
||||
game = Node.void("game_3")
|
||||
new = Node.void("new")
|
||||
game.add_child(new)
|
||||
|
||||
for score in scores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
new.add_child(music)
|
||||
music.add_child(Node.u32('music_id', score.id))
|
||||
music.add_child(Node.u32('music_type', score.chart))
|
||||
music.add_child(Node.u32('score', score.points))
|
||||
music.add_child(Node.u32('cnt', score.plays))
|
||||
music.add_child(Node.u32('clear_type', self.db_to_game_clear_type(score.data.get_int('clear_type'))))
|
||||
music.add_child(Node.u32('score_grade', self.db_to_game_grade(score.data.get_int('grade'))))
|
||||
stats = score.data.get_dict('stats')
|
||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
||||
music.add_child(Node.u32('long_rate', stats.get_int('long_rate')))
|
||||
music.add_child(Node.u32('vol_rate', stats.get_int('vol_rate')))
|
||||
music.add_child(Node.u32("music_id", score.id))
|
||||
music.add_child(Node.u32("music_type", score.chart))
|
||||
music.add_child(Node.u32("score", score.points))
|
||||
music.add_child(Node.u32("cnt", score.plays))
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"clear_type",
|
||||
self.db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||
)
|
||||
)
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"score_grade", self.db_to_game_grade(score.data.get_int("grade"))
|
||||
)
|
||||
)
|
||||
stats = score.data.get_dict("stats")
|
||||
music.add_child(Node.u32("btn_rate", stats.get_int("btn_rate")))
|
||||
music.add_child(Node.u32("long_rate", stats.get_int("long_rate")))
|
||||
music.add_child(Node.u32("vol_rate", stats.get_int("vol_rate")))
|
||||
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
|
||||
# Generic profile stuff
|
||||
game.add_child(Node.string('name', profile.get_str('name')))
|
||||
game.add_child(Node.string('code', ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
||||
game.add_child(Node.u32('gamecoin_block', profile.get_int('block')))
|
||||
game.add_child(Node.s16('skill_name_id', profile.get_int('skill_name_id', -1)))
|
||||
game.add_child(Node.s32_array('hidden_param', profile.get_int_array('hidden_param', 20)))
|
||||
game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy')))
|
||||
game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count')))
|
||||
game.add_child(Node.string("name", profile.get_str("name")))
|
||||
game.add_child(Node.string("code", ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32("gamecoin_packet", profile.get_int("packet")))
|
||||
game.add_child(Node.u32("gamecoin_block", profile.get_int("block")))
|
||||
game.add_child(Node.s16("skill_name_id", profile.get_int("skill_name_id", -1)))
|
||||
game.add_child(
|
||||
Node.s32_array("hidden_param", profile.get_int_array("hidden_param", 20))
|
||||
)
|
||||
game.add_child(Node.u32("blaster_energy", profile.get_int("blaster_energy")))
|
||||
game.add_child(Node.u32("blaster_count", profile.get_int("blaster_count")))
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
game.add_child(Node.u32("play_count", statistics.total_plays))
|
||||
game.add_child(Node.u32("daily_count", statistics.today_plays))
|
||||
game.add_child(Node.u32("play_chain", statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
lastdict = profile.get_dict('last')
|
||||
last = Node.void('last')
|
||||
if "last" in profile:
|
||||
lastdict = profile.get_dict("last")
|
||||
last = Node.void("last")
|
||||
game.add_child(last)
|
||||
last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1)))
|
||||
last.add_child(Node.u8('music_type', lastdict.get_int('music_type')))
|
||||
last.add_child(Node.u8('sort_type', lastdict.get_int('sort_type')))
|
||||
last.add_child(Node.u8('narrow_down', lastdict.get_int('narrow_down')))
|
||||
last.add_child(Node.u8('headphone', lastdict.get_int('headphone')))
|
||||
last.add_child(Node.u16('appeal_id', lastdict.get_int('appeal_id', 1001)))
|
||||
last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id')))
|
||||
last.add_child(Node.u8('gauge_option', lastdict.get_int('gauge_option')))
|
||||
last.add_child(Node.s32("music_id", lastdict.get_int("music_id", -1)))
|
||||
last.add_child(Node.u8("music_type", lastdict.get_int("music_type")))
|
||||
last.add_child(Node.u8("sort_type", lastdict.get_int("sort_type")))
|
||||
last.add_child(Node.u8("narrow_down", lastdict.get_int("narrow_down")))
|
||||
last.add_child(Node.u8("headphone", lastdict.get_int("headphone")))
|
||||
last.add_child(Node.u16("appeal_id", lastdict.get_int("appeal_id", 1001)))
|
||||
last.add_child(Node.u16("comment_id", lastdict.get_int("comment_id")))
|
||||
last.add_child(Node.u8("gauge_option", lastdict.get_int("gauge_option")))
|
||||
|
||||
# Item unlocks
|
||||
itemnode = Node.void('item')
|
||||
itemnode = Node.void("item")
|
||||
game.add_child(itemnode)
|
||||
|
||||
game_config = self.get_game_config()
|
||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||
achievements = self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
|
||||
for item in achievements:
|
||||
if item.type[:5] != 'item_':
|
||||
if item.type[:5] != "item_":
|
||||
continue
|
||||
itemtype = int(item.type[5:])
|
||||
|
||||
if game_config.get_bool('force_unlock_songs') and itemtype == self.GAME_CATALOG_TYPE_SONG:
|
||||
if (
|
||||
game_config.get_bool("force_unlock_songs")
|
||||
and itemtype == self.GAME_CATALOG_TYPE_SONG
|
||||
):
|
||||
# Don't echo unlocked songs, we will add all of them later
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
itemnode.add_child(info)
|
||||
info.add_child(Node.u8('type', itemtype))
|
||||
info.add_child(Node.u32('id', item.id))
|
||||
info.add_child(Node.u32('param', item.data.get_int('param')))
|
||||
if 'diff_param' in item.data:
|
||||
info.add_child(Node.s32('diff_param', item.data.get_int('diff_param')))
|
||||
info.add_child(Node.u8("type", itemtype))
|
||||
info.add_child(Node.u32("id", item.id))
|
||||
info.add_child(Node.u32("param", item.data.get_int("param")))
|
||||
if "diff_param" in item.data:
|
||||
info.add_child(Node.s32("diff_param", item.data.get_int("diff_param")))
|
||||
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids: Dict[int, int] = {}
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||
for song in songs:
|
||||
if song.id not in ids:
|
||||
ids[song.id] = 0
|
||||
|
||||
if song.data.get_int('difficulty') > 0:
|
||||
if song.data.get_int("difficulty") > 0:
|
||||
ids[song.id] = ids[song.id] | (1 << song.chart)
|
||||
|
||||
for itemid in ids:
|
||||
if ids[itemid] == 0:
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
itemnode.add_child(info)
|
||||
info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG))
|
||||
info.add_child(Node.u32('id', itemid))
|
||||
info.add_child(Node.u32('param', ids[itemid]))
|
||||
info.add_child(Node.u8("type", self.GAME_CATALOG_TYPE_SONG))
|
||||
info.add_child(Node.u32("id", itemid))
|
||||
info.add_child(Node.u32("param", ids[itemid]))
|
||||
|
||||
return game
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
|
||||
# Update blaster energy and in-game currencies
|
||||
earned_gamecoin_packet = request.child_value('earned_gamecoin_packet')
|
||||
earned_gamecoin_packet = request.child_value("earned_gamecoin_packet")
|
||||
if earned_gamecoin_packet is not None:
|
||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
||||
newprofile.replace_int(
|
||||
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||
)
|
||||
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||
if earned_gamecoin_block is not None:
|
||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
||||
earned_blaster_energy = request.child_value('earned_blaster_energy')
|
||||
newprofile.replace_int(
|
||||
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||
)
|
||||
earned_blaster_energy = request.child_value("earned_blaster_energy")
|
||||
if earned_blaster_energy is not None:
|
||||
newprofile.replace_int('blaster_energy', newprofile.get_int('blaster_energy') + earned_blaster_energy)
|
||||
newprofile.replace_int(
|
||||
"blaster_energy",
|
||||
newprofile.get_int("blaster_energy") + earned_blaster_energy,
|
||||
)
|
||||
|
||||
# Miscelaneous stuff
|
||||
newprofile.replace_int('blaster_count', request.child_value('blaster_count'))
|
||||
newprofile.replace_int('skill_name_id', request.child_value('skill_name_id'))
|
||||
newprofile.replace_int_array('hidden_param', 20, request.child_value('hidden_param'))
|
||||
newprofile.replace_int("blaster_count", request.child_value("blaster_count"))
|
||||
newprofile.replace_int("skill_name_id", request.child_value("skill_name_id"))
|
||||
newprofile.replace_int_array(
|
||||
"hidden_param", 20, request.child_value("hidden_param")
|
||||
)
|
||||
|
||||
# Update user's unlock status if we aren't force unlocked
|
||||
game_config = self.get_game_config()
|
||||
|
||||
if request.child('item') is not None:
|
||||
for child in request.child('item').children:
|
||||
if child.name != 'info':
|
||||
if request.child("item") is not None:
|
||||
for child in request.child("item").children:
|
||||
if child.name != "info":
|
||||
continue
|
||||
|
||||
item_id = child.child_value('id')
|
||||
item_type = child.child_value('type')
|
||||
param = child.child_value('param')
|
||||
diff_param = child.child_value('diff_param')
|
||||
item_id = child.child_value("id")
|
||||
item_type = child.child_value("type")
|
||||
param = child.child_value("param")
|
||||
diff_param = child.child_value("diff_param")
|
||||
|
||||
if game_config.get_bool('force_unlock_songs') and item_type == self.GAME_CATALOG_TYPE_SONG:
|
||||
if (
|
||||
game_config.get_bool("force_unlock_songs")
|
||||
and item_type == self.GAME_CATALOG_TYPE_SONG
|
||||
):
|
||||
# Don't save back songs, because they were force unlocked
|
||||
continue
|
||||
|
||||
if diff_param is not None:
|
||||
paramvals = {
|
||||
'diff_param': diff_param,
|
||||
'param': param,
|
||||
"diff_param": diff_param,
|
||||
"param": param,
|
||||
}
|
||||
else:
|
||||
paramvals = {
|
||||
'param': param,
|
||||
"param": param,
|
||||
}
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
@ -330,23 +365,23 @@ class Museca1(
|
||||
self.version,
|
||||
userid,
|
||||
item_id,
|
||||
f'item_{item_type}',
|
||||
f"item_{item_type}",
|
||||
paramvals,
|
||||
)
|
||||
|
||||
# Grab last information.
|
||||
lastdict = newprofile.get_dict('last')
|
||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
||||
lastdict.replace_int('comment_id', request.child_value('comment_id'))
|
||||
lastdict.replace_int('music_id', request.child_value('music_id'))
|
||||
lastdict.replace_int('music_type', request.child_value('music_type'))
|
||||
lastdict.replace_int('sort_type', request.child_value('sort_type'))
|
||||
lastdict.replace_int('narrow_down', request.child_value('narrow_down'))
|
||||
lastdict.replace_int('gauge_option', request.child_value('gauge_option'))
|
||||
lastdict = newprofile.get_dict("last")
|
||||
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||
lastdict.replace_int("comment_id", request.child_value("comment_id"))
|
||||
lastdict.replace_int("music_id", request.child_value("music_id"))
|
||||
lastdict.replace_int("music_type", request.child_value("music_type"))
|
||||
lastdict.replace_int("sort_type", request.child_value("sort_type"))
|
||||
lastdict.replace_int("narrow_down", request.child_value("narrow_down"))
|
||||
lastdict.replace_int("gauge_option", request.child_value("gauge_option"))
|
||||
|
||||
# Save back last information gleaned from results
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid)
|
||||
|
@ -64,18 +64,18 @@ class Museca1Plus(
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Disable Online Matching',
|
||||
'tip': 'Disable online matching between games.',
|
||||
'category': 'game_config',
|
||||
'setting': 'disable_matching',
|
||||
"name": "Disable Online Matching",
|
||||
"tip": "Disable online matching between games.",
|
||||
"category": "game_config",
|
||||
"setting": "disable_matching",
|
||||
},
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -121,36 +121,39 @@ class Museca1Plus(
|
||||
}[grade]
|
||||
|
||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
limited = Node.void('music_limited')
|
||||
game = Node.void("game_3")
|
||||
limited = Node.void("music_limited")
|
||||
game.add_child(limited)
|
||||
|
||||
# Song unlock config
|
||||
game_config = self.get_game_config()
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids = set()
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||
for song in songs:
|
||||
if song.data.get_int('limited') in (self.GAME_LIMITED_LOCKED, self.GAME_LIMITED_UNLOCKABLE):
|
||||
if song.data.get_int("limited") in (
|
||||
self.GAME_LIMITED_LOCKED,
|
||||
self.GAME_LIMITED_UNLOCKABLE,
|
||||
):
|
||||
ids.add((song.id, song.chart))
|
||||
|
||||
for (songid, chart) in ids:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
limited.add_child(info)
|
||||
info.add_child(Node.s32('music_id', songid))
|
||||
info.add_child(Node.u8('music_type', chart))
|
||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
||||
info.add_child(Node.s32("music_id", songid))
|
||||
info.add_child(Node.u8("music_type", chart))
|
||||
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||
|
||||
# Event config
|
||||
event = Node.void('event')
|
||||
event = Node.void("event")
|
||||
game.add_child(event)
|
||||
|
||||
def enable_event(eid: int) -> None:
|
||||
evt = Node.void('info')
|
||||
evt = Node.void("info")
|
||||
event.add_child(evt)
|
||||
evt.add_child(Node.u32('event_id', eid))
|
||||
evt.add_child(Node.u32("event_id", eid))
|
||||
|
||||
if not game_config.get_bool('disable_matching'):
|
||||
if not game_config.get_bool("disable_matching"):
|
||||
enable_event(143) # Matching enabled
|
||||
|
||||
# These events are meant specifically for Museca Plus
|
||||
@ -160,11 +163,11 @@ class Museca1Plus(
|
||||
212, # News 2
|
||||
]
|
||||
event_ids = [
|
||||
1, # Extended pedal options (no effect on Museca 1+1/2)
|
||||
56, # Generator grafica icon <print 1 in musicdb>
|
||||
83, # Paseli Light Start
|
||||
86, # Generator grafica icon <print 2 in musicdb>
|
||||
98, # Caption 2 notice (grs_grafica_caption_2.png)
|
||||
1, # Extended pedal options (no effect on Museca 1+1/2)
|
||||
56, # Generator grafica icon <print 1 in musicdb>
|
||||
83, # Paseli Light Start
|
||||
86, # Generator grafica icon <print 2 in musicdb>
|
||||
98, # Caption 2 notice (grs_grafica_caption_2.png)
|
||||
105, # Makes the "Number of Layers" option visible in game settings
|
||||
130, # Curator Rank
|
||||
141, # Coconatsu & Mukipara grafica effects
|
||||
@ -183,96 +186,96 @@ class Museca1Plus(
|
||||
enable_event(evtid)
|
||||
|
||||
# List of known event IDs:
|
||||
# 56, # Generator grafica icon <print 1 in musicdb>
|
||||
# 83, # Paseli Light Start
|
||||
# 86, # Generator grafica icon <print 2 in musicdb>
|
||||
# 98, # Caption 2 notice (grs_grafica_caption_2.png)
|
||||
# 100, # DJ YOSHITAKA EXHIBITION 2016
|
||||
# 103, # HATSUNE MIKU EXHIBITION 2016 - PART 1
|
||||
# 104, # HATSUNE MIKU EXHIBITION 2016 - PART 2
|
||||
# 105, # Makes the "Number of Layers" option visible in game settings
|
||||
# 106, # HATSUNE MIKU EXHIBITION 2016 - PART 3
|
||||
# 117, # NEW GENERATION METEOR DIFFUSE FESTA 2016 / RYUSEI FESTA TRIGGER
|
||||
# 129, # COCONATSU EXHIBITION 2016
|
||||
# 130, # Curator Rank
|
||||
# 97, # Agetta Moratta (vmlink_phase 1 in musicdb)
|
||||
# 114, # Agetta Moratta (vmlink_phase 2 in musicdb)
|
||||
# 140, # Agetta Moratta (vmlink_phase 3 in musicdb)
|
||||
# 141, # Coconatsu & Mukipara grafica effects
|
||||
# 143, # Matching
|
||||
# 144, # BEMANI ARCHAEOLOGICAL EXHIBITION
|
||||
# 163, # TUTORIAL SNOW
|
||||
# 169, # SHIORI FUJISAKI EXHIBITION 2017 - PART 1
|
||||
# 174, # SHIORI FUJISAKI EXHIBITION 2017 - PART 2
|
||||
# 182, # Mute illil's voice?
|
||||
# 192, # GREAT REPRINT FESTIVAL: MIKU + DJ YOSHITAKA
|
||||
# 194, # Continue
|
||||
# 195, # Fictional Curator (foot pedal options)
|
||||
# 211, #News 1
|
||||
# 212, #News 2
|
||||
# 213, #News 3
|
||||
# 214, #News 4
|
||||
# 217, #News 5
|
||||
# 218, #News 6
|
||||
# 219, #News 7
|
||||
# 220, #News 8
|
||||
# 221, # GRAFICA PRESENTATION CAMPAIGN “THE PRIMITIVE LIFE EXHIBITION”
|
||||
# 222, # GRAFICA PRESENTATION CAMPAIGN "NOISE"
|
||||
# 223, # GRAFICA PRESENTATION CAMPAIGN "PATISSERIE ROUGE"
|
||||
# 224, # GRAFICA PRESENTATION CAMPAIGN "GUNSLINGER"
|
||||
# 145, # MUKIPARA UNLOCKS
|
||||
# 146, # MUKIPARA UNLOCKS
|
||||
# 147, # MUKIPARA UNLOCKS
|
||||
# 148, # MUKIPARA UNLOCKS
|
||||
# 149, # MUKIPARA UNLOCKS
|
||||
# 56, # Generator grafica icon <print 1 in musicdb>
|
||||
# 83, # Paseli Light Start
|
||||
# 86, # Generator grafica icon <print 2 in musicdb>
|
||||
# 98, # Caption 2 notice (grs_grafica_caption_2.png)
|
||||
# 100, # DJ YOSHITAKA EXHIBITION 2016
|
||||
# 103, # HATSUNE MIKU EXHIBITION 2016 - PART 1
|
||||
# 104, # HATSUNE MIKU EXHIBITION 2016 - PART 2
|
||||
# 105, # Makes the "Number of Layers" option visible in game settings
|
||||
# 106, # HATSUNE MIKU EXHIBITION 2016 - PART 3
|
||||
# 117, # NEW GENERATION METEOR DIFFUSE FESTA 2016 / RYUSEI FESTA TRIGGER
|
||||
# 129, # COCONATSU EXHIBITION 2016
|
||||
# 130, # Curator Rank
|
||||
# 97, # Agetta Moratta (vmlink_phase 1 in musicdb)
|
||||
# 114, # Agetta Moratta (vmlink_phase 2 in musicdb)
|
||||
# 140, # Agetta Moratta (vmlink_phase 3 in musicdb)
|
||||
# 141, # Coconatsu & Mukipara grafica effects
|
||||
# 143, # Matching
|
||||
# 144, # BEMANI ARCHAEOLOGICAL EXHIBITION
|
||||
# 163, # TUTORIAL SNOW
|
||||
# 169, # SHIORI FUJISAKI EXHIBITION 2017 - PART 1
|
||||
# 174, # SHIORI FUJISAKI EXHIBITION 2017 - PART 2
|
||||
# 182, # Mute illil's voice?
|
||||
# 192, # GREAT REPRINT FESTIVAL: MIKU + DJ YOSHITAKA
|
||||
# 194, # Continue
|
||||
# 195, # Fictional Curator (foot pedal options)
|
||||
# 211, #News 1
|
||||
# 212, #News 2
|
||||
# 213, #News 3
|
||||
# 214, #News 4
|
||||
# 217, #News 5
|
||||
# 218, #News 6
|
||||
# 219, #News 7
|
||||
# 220, #News 8
|
||||
# 221, # GRAFICA PRESENTATION CAMPAIGN “THE PRIMITIVE LIFE EXHIBITION”
|
||||
# 222, # GRAFICA PRESENTATION CAMPAIGN "NOISE"
|
||||
# 223, # GRAFICA PRESENTATION CAMPAIGN "PATISSERIE ROUGE"
|
||||
# 224, # GRAFICA PRESENTATION CAMPAIGN "GUNSLINGER"
|
||||
# 145, # MUKIPARA UNLOCKS
|
||||
# 146, # MUKIPARA UNLOCKS
|
||||
# 147, # MUKIPARA UNLOCKS
|
||||
# 148, # MUKIPARA UNLOCKS
|
||||
# 149, # MUKIPARA UNLOCKS
|
||||
|
||||
# Makes special missions available on grafica that have them.
|
||||
extend = Node.void('extend')
|
||||
extend = Node.void("extend")
|
||||
game.add_child(extend)
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
extend.add_child(info)
|
||||
info.add_child(Node.u32('extend_id', 1))
|
||||
info.add_child(Node.u32('extend_type', 9))
|
||||
info.add_child(Node.s32('param_num_1', 2))
|
||||
info.add_child(Node.s32('param_num_2', 50))
|
||||
info.add_child(Node.s32('param_num_3', 59))
|
||||
info.add_child(Node.s32('param_num_4', 64))
|
||||
info.add_child(Node.s32('param_num_5', 86))
|
||||
info.add_child(Node.string('param_str_1', 'available_ex: 1'))
|
||||
info.add_child(Node.string('param_str_2', 'available_ex: 1'))
|
||||
info.add_child(Node.string('param_str_3', 'available_ex: 1'))
|
||||
info.add_child(Node.string('param_str_4', 'available_ex: 1'))
|
||||
info.add_child(Node.string('param_str_5', 'available_ex: 1'))
|
||||
info.add_child(Node.u32("extend_id", 1))
|
||||
info.add_child(Node.u32("extend_type", 9))
|
||||
info.add_child(Node.s32("param_num_1", 2))
|
||||
info.add_child(Node.s32("param_num_2", 50))
|
||||
info.add_child(Node.s32("param_num_3", 59))
|
||||
info.add_child(Node.s32("param_num_4", 64))
|
||||
info.add_child(Node.s32("param_num_5", 86))
|
||||
info.add_child(Node.string("param_str_1", "available_ex: 1"))
|
||||
info.add_child(Node.string("param_str_2", "available_ex: 1"))
|
||||
info.add_child(Node.string("param_str_3", "available_ex: 1"))
|
||||
info.add_child(Node.string("param_str_4", "available_ex: 1"))
|
||||
info.add_child(Node.string("param_str_5", "available_ex: 1"))
|
||||
|
||||
if self.omnimix:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
extend.add_child(info)
|
||||
info.add_child(Node.u32('extend_id', 2))
|
||||
info.add_child(Node.u32('extend_type', 9))
|
||||
info.add_child(Node.s32('param_num_1', 210))
|
||||
info.add_child(Node.s32('param_num_2', 0))
|
||||
info.add_child(Node.s32('param_num_3', 0))
|
||||
info.add_child(Node.s32('param_num_4', 0))
|
||||
info.add_child(Node.s32('param_num_5', 0))
|
||||
info.add_child(Node.string('param_str_1', ''))
|
||||
info.add_child(Node.string('param_str_2', ''))
|
||||
info.add_child(Node.string('param_str_3', ''))
|
||||
info.add_child(Node.string('param_str_4', ''))
|
||||
info.add_child(Node.string('param_str_5', ''))
|
||||
info.add_child(Node.u32("extend_id", 2))
|
||||
info.add_child(Node.u32("extend_type", 9))
|
||||
info.add_child(Node.s32("param_num_1", 210))
|
||||
info.add_child(Node.s32("param_num_2", 0))
|
||||
info.add_child(Node.s32("param_num_3", 0))
|
||||
info.add_child(Node.s32("param_num_4", 0))
|
||||
info.add_child(Node.s32("param_num_5", 0))
|
||||
info.add_child(Node.string("param_str_1", ""))
|
||||
info.add_child(Node.string("param_str_2", ""))
|
||||
info.add_child(Node.string("param_str_3", ""))
|
||||
info.add_child(Node.string("param_str_4", ""))
|
||||
info.add_child(Node.string("param_str_5", ""))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
# Refresh interval in seconds.
|
||||
game.add_child(Node.u32('interval', 10))
|
||||
game.add_child(Node.u32("interval", 10))
|
||||
return game
|
||||
|
||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_load_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
root = self.get_profile_by_refid(refid)
|
||||
if root is not None:
|
||||
return root
|
||||
@ -294,12 +297,12 @@ class Museca1Plus(
|
||||
# Return the previous formatted profile to the game.
|
||||
return previous_game.format_profile(userid, profile)
|
||||
else:
|
||||
root = Node.void('game_3')
|
||||
root.add_child(Node.u8('result', 1))
|
||||
root = Node.void("game_3")
|
||||
root.add_child(Node.u8("result", 1))
|
||||
return root
|
||||
|
||||
def handle_game_3_load_m_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('dataid')
|
||||
refid = request.child_value("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -307,160 +310,192 @@ class Museca1Plus(
|
||||
userid = None
|
||||
|
||||
if userid is not None:
|
||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
||||
scores = self.data.remote.music.get_scores(
|
||||
self.game, self.music_version, userid
|
||||
)
|
||||
else:
|
||||
scores = []
|
||||
|
||||
# Output to the game
|
||||
game = Node.void('game_3')
|
||||
new = Node.void('new')
|
||||
game = Node.void("game_3")
|
||||
new = Node.void("new")
|
||||
game.add_child(new)
|
||||
|
||||
for score in scores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
new.add_child(music)
|
||||
music.add_child(Node.u32('music_id', score.id))
|
||||
music.add_child(Node.u32('music_type', score.chart))
|
||||
music.add_child(Node.u32('score', score.points))
|
||||
music.add_child(Node.u32('cnt', score.plays))
|
||||
music.add_child(Node.u32('combo', score.data.get_int('combo')))
|
||||
music.add_child(Node.u32('clear_type', self.db_to_game_clear_type(score.data.get_int('clear_type'))))
|
||||
music.add_child(Node.u32('score_grade', self.db_to_game_grade(score.data.get_int('grade'))))
|
||||
stats = score.data.get_dict('stats')
|
||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
||||
music.add_child(Node.u32('long_rate', stats.get_int('long_rate')))
|
||||
music.add_child(Node.u32('vol_rate', stats.get_int('vol_rate')))
|
||||
music.add_child(Node.u32("music_id", score.id))
|
||||
music.add_child(Node.u32("music_type", score.chart))
|
||||
music.add_child(Node.u32("score", score.points))
|
||||
music.add_child(Node.u32("cnt", score.plays))
|
||||
music.add_child(Node.u32("combo", score.data.get_int("combo")))
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"clear_type",
|
||||
self.db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||
)
|
||||
)
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"score_grade", self.db_to_game_grade(score.data.get_int("grade"))
|
||||
)
|
||||
)
|
||||
stats = score.data.get_dict("stats")
|
||||
music.add_child(Node.u32("btn_rate", stats.get_int("btn_rate")))
|
||||
music.add_child(Node.u32("long_rate", stats.get_int("long_rate")))
|
||||
music.add_child(Node.u32("vol_rate", stats.get_int("vol_rate")))
|
||||
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
|
||||
# Generic profile stuff
|
||||
game.add_child(Node.string('name', profile.get_str('name')))
|
||||
game.add_child(Node.string('code', ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
||||
game.add_child(Node.u32('gamecoin_block', profile.get_int('block')))
|
||||
game.add_child(Node.s16('skill_name_id', profile.get_int('skill_name_id', -1)))
|
||||
game.add_child(Node.s32_array('hidden_param', profile.get_int_array('hidden_param', 20)))
|
||||
game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy')))
|
||||
game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count')))
|
||||
game.add_child(Node.string("name", profile.get_str("name")))
|
||||
game.add_child(Node.string("code", ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32("gamecoin_packet", profile.get_int("packet")))
|
||||
game.add_child(Node.u32("gamecoin_block", profile.get_int("block")))
|
||||
game.add_child(Node.s16("skill_name_id", profile.get_int("skill_name_id", -1)))
|
||||
game.add_child(
|
||||
Node.s32_array("hidden_param", profile.get_int_array("hidden_param", 20))
|
||||
)
|
||||
game.add_child(Node.u32("blaster_energy", profile.get_int("blaster_energy")))
|
||||
game.add_child(Node.u32("blaster_count", profile.get_int("blaster_count")))
|
||||
|
||||
# Enable Ryusei Festa
|
||||
ryusei_festa = Node.void('ryusei_festa')
|
||||
ryusei_festa = Node.void("ryusei_festa")
|
||||
game.add_child(ryusei_festa)
|
||||
ryusei_festa.add_child(Node.bool('ryusei_festa_trigger', True))
|
||||
ryusei_festa.add_child(Node.bool("ryusei_festa_trigger", True))
|
||||
|
||||
# Play statistics
|
||||
statistics = self.get_play_statistics(userid)
|
||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
||||
game.add_child(Node.u32('daily_count', statistics.today_plays))
|
||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
||||
game.add_child(Node.u32("play_count", statistics.total_plays))
|
||||
game.add_child(Node.u32("daily_count", statistics.today_plays))
|
||||
game.add_child(Node.u32("play_chain", statistics.consecutive_days))
|
||||
|
||||
# Last played stuff
|
||||
if 'last' in profile:
|
||||
lastdict = profile.get_dict('last')
|
||||
last = Node.void('last')
|
||||
if "last" in profile:
|
||||
lastdict = profile.get_dict("last")
|
||||
last = Node.void("last")
|
||||
game.add_child(last)
|
||||
last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1)))
|
||||
last.add_child(Node.u8('music_type', lastdict.get_int('music_type')))
|
||||
last.add_child(Node.u8('sort_type', lastdict.get_int('sort_type')))
|
||||
last.add_child(Node.u8('narrow_down', lastdict.get_int('narrow_down')))
|
||||
last.add_child(Node.u8('headphone', lastdict.get_int('headphone')))
|
||||
last.add_child(Node.u16('appeal_id', lastdict.get_int('appeal_id', 1001)))
|
||||
last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id')))
|
||||
last.add_child(Node.u8('gauge_option', lastdict.get_int('gauge_option')))
|
||||
last.add_child(Node.s32("music_id", lastdict.get_int("music_id", -1)))
|
||||
last.add_child(Node.u8("music_type", lastdict.get_int("music_type")))
|
||||
last.add_child(Node.u8("sort_type", lastdict.get_int("sort_type")))
|
||||
last.add_child(Node.u8("narrow_down", lastdict.get_int("narrow_down")))
|
||||
last.add_child(Node.u8("headphone", lastdict.get_int("headphone")))
|
||||
last.add_child(Node.u16("appeal_id", lastdict.get_int("appeal_id", 1001)))
|
||||
last.add_child(Node.u16("comment_id", lastdict.get_int("comment_id")))
|
||||
last.add_child(Node.u8("gauge_option", lastdict.get_int("gauge_option")))
|
||||
|
||||
# Item unlocks
|
||||
itemnode = Node.void('item')
|
||||
itemnode = Node.void("item")
|
||||
game.add_child(itemnode)
|
||||
|
||||
game_config = self.get_game_config()
|
||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||
achievements = self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
|
||||
for item in achievements:
|
||||
if item.type[:5] != 'item_':
|
||||
if item.type[:5] != "item_":
|
||||
continue
|
||||
itemtype = int(item.type[5:])
|
||||
|
||||
if game_config.get_bool('force_unlock_songs') and itemtype == self.GAME_CATALOG_TYPE_SONG:
|
||||
if (
|
||||
game_config.get_bool("force_unlock_songs")
|
||||
and itemtype == self.GAME_CATALOG_TYPE_SONG
|
||||
):
|
||||
# Don't echo unlocked songs, we will add all of them later
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
itemnode.add_child(info)
|
||||
info.add_child(Node.u8('type', itemtype))
|
||||
info.add_child(Node.u32('id', item.id))
|
||||
info.add_child(Node.u32('param', item.data.get_int('param')))
|
||||
if 'diff_param' in item.data:
|
||||
info.add_child(Node.s32('diff_param', item.data.get_int('diff_param')))
|
||||
info.add_child(Node.u8("type", itemtype))
|
||||
info.add_child(Node.u32("id", item.id))
|
||||
info.add_child(Node.u32("param", item.data.get_int("param")))
|
||||
if "diff_param" in item.data:
|
||||
info.add_child(Node.s32("diff_param", item.data.get_int("diff_param")))
|
||||
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids: Dict[int, int] = {}
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||
for song in songs:
|
||||
if song.id not in ids:
|
||||
ids[song.id] = 0
|
||||
|
||||
if song.data.get_int('difficulty') > 0:
|
||||
if song.data.get_int("difficulty") > 0:
|
||||
ids[song.id] = ids[song.id] | (1 << song.chart)
|
||||
|
||||
for itemid in ids:
|
||||
if ids[itemid] == 0:
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
itemnode.add_child(info)
|
||||
info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG))
|
||||
info.add_child(Node.u32('id', itemid))
|
||||
info.add_child(Node.u32('param', ids[itemid]))
|
||||
info.add_child(Node.u8("type", self.GAME_CATALOG_TYPE_SONG))
|
||||
info.add_child(Node.u32("id", itemid))
|
||||
info.add_child(Node.u32("param", ids[itemid]))
|
||||
|
||||
return game
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
|
||||
# Update blaster energy and in-game currencies
|
||||
earned_gamecoin_packet = request.child_value('earned_gamecoin_packet')
|
||||
earned_gamecoin_packet = request.child_value("earned_gamecoin_packet")
|
||||
if earned_gamecoin_packet is not None:
|
||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
||||
newprofile.replace_int(
|
||||
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||
)
|
||||
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||
if earned_gamecoin_block is not None:
|
||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
||||
earned_blaster_energy = request.child_value('earned_blaster_energy')
|
||||
newprofile.replace_int(
|
||||
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||
)
|
||||
earned_blaster_energy = request.child_value("earned_blaster_energy")
|
||||
if earned_blaster_energy is not None:
|
||||
newprofile.replace_int('blaster_energy', newprofile.get_int('blaster_energy') + earned_blaster_energy)
|
||||
newprofile.replace_int(
|
||||
"blaster_energy",
|
||||
newprofile.get_int("blaster_energy") + earned_blaster_energy,
|
||||
)
|
||||
|
||||
# Miscelaneous stuff
|
||||
newprofile.replace_int('blaster_count', request.child_value('blaster_count'))
|
||||
newprofile.replace_int('skill_name_id', request.child_value('skill_name_id'))
|
||||
newprofile.replace_int_array('hidden_param', 20, request.child_value('hidden_param'))
|
||||
newprofile.replace_int("blaster_count", request.child_value("blaster_count"))
|
||||
newprofile.replace_int("skill_name_id", request.child_value("skill_name_id"))
|
||||
newprofile.replace_int_array(
|
||||
"hidden_param", 20, request.child_value("hidden_param")
|
||||
)
|
||||
|
||||
# Update user's unlock status if we aren't force unlocked
|
||||
game_config = self.get_game_config()
|
||||
|
||||
if request.child('item') is not None:
|
||||
for child in request.child('item').children:
|
||||
if child.name != 'info':
|
||||
if request.child("item") is not None:
|
||||
for child in request.child("item").children:
|
||||
if child.name != "info":
|
||||
continue
|
||||
|
||||
item_id = child.child_value('id')
|
||||
item_type = child.child_value('type')
|
||||
param = child.child_value('param')
|
||||
diff_param = child.child_value('diff_param')
|
||||
item_id = child.child_value("id")
|
||||
item_type = child.child_value("type")
|
||||
param = child.child_value("param")
|
||||
diff_param = child.child_value("diff_param")
|
||||
|
||||
if game_config.get_bool('force_unlock_songs') and item_type == self.GAME_CATALOG_TYPE_SONG:
|
||||
if (
|
||||
game_config.get_bool("force_unlock_songs")
|
||||
and item_type == self.GAME_CATALOG_TYPE_SONG
|
||||
):
|
||||
# Don't save back songs, because they were force unlocked
|
||||
continue
|
||||
|
||||
if diff_param is not None:
|
||||
paramvals = {
|
||||
'diff_param': diff_param,
|
||||
'param': param,
|
||||
"diff_param": diff_param,
|
||||
"param": param,
|
||||
}
|
||||
else:
|
||||
paramvals = {
|
||||
'param': param,
|
||||
"param": param,
|
||||
}
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
@ -468,23 +503,23 @@ class Museca1Plus(
|
||||
self.version,
|
||||
userid,
|
||||
item_id,
|
||||
f'item_{item_type}',
|
||||
f"item_{item_type}",
|
||||
paramvals,
|
||||
)
|
||||
|
||||
# Grab last information.
|
||||
lastdict = newprofile.get_dict('last')
|
||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
||||
lastdict.replace_int('comment_id', request.child_value('comment_id'))
|
||||
lastdict.replace_int('music_id', request.child_value('music_id'))
|
||||
lastdict.replace_int('music_type', request.child_value('music_type'))
|
||||
lastdict.replace_int('sort_type', request.child_value('sort_type'))
|
||||
lastdict.replace_int('narrow_down', request.child_value('narrow_down'))
|
||||
lastdict.replace_int('gauge_option', request.child_value('gauge_option'))
|
||||
lastdict = newprofile.get_dict("last")
|
||||
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||
lastdict.replace_int("comment_id", request.child_value("comment_id"))
|
||||
lastdict.replace_int("music_id", request.child_value("music_id"))
|
||||
lastdict.replace_int("music_type", request.child_value("music_type"))
|
||||
lastdict.replace_int("sort_type", request.child_value("sort_type"))
|
||||
lastdict.replace_int("narrow_down", request.child_value("narrow_down"))
|
||||
lastdict.replace_int("gauge_option", request.child_value("gauge_option"))
|
||||
|
||||
# Save back last information gleaned from results
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid)
|
||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
||||
from bemani.common import Profile, ValidatedDict, Time, GameConstants, DBConstants, BroadcastConstants
|
||||
from bemani.common import (
|
||||
Profile,
|
||||
ValidatedDict,
|
||||
Time,
|
||||
GameConstants,
|
||||
DBConstants,
|
||||
BroadcastConstants,
|
||||
)
|
||||
from bemani.data import UserID, Achievement, ScoreSaveException
|
||||
from bemani.protocol import Node
|
||||
|
||||
@ -20,16 +27,30 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
|
||||
# Play medals, as saved into/loaded from the DB
|
||||
PLAY_MEDAL_NO_PLAY: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_NO_PLAY
|
||||
PLAY_MEDAL_CIRCLE_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED
|
||||
PLAY_MEDAL_DIAMOND_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED
|
||||
PLAY_MEDAL_CIRCLE_FAILED: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED
|
||||
PLAY_MEDAL_DIAMOND_FAILED: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED
|
||||
PLAY_MEDAL_STAR_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED
|
||||
PLAY_MEDAL_EASY_CLEAR: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR
|
||||
PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED
|
||||
PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED
|
||||
PLAY_MEDAL_CIRCLE_CLEARED: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED
|
||||
PLAY_MEDAL_DIAMOND_CLEARED: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED
|
||||
PLAY_MEDAL_STAR_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED
|
||||
PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO
|
||||
PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO
|
||||
PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO
|
||||
PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO
|
||||
PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO
|
||||
PLAY_MEDAL_STAR_FULL_COMBO: Final[
|
||||
int
|
||||
] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO
|
||||
PLAY_MEDAL_PERFECT: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT
|
||||
|
||||
# Chart type, as saved into/loaded from the DB, and returned to game
|
||||
@ -47,7 +68,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# properly.
|
||||
supports_expired_profiles: bool = False
|
||||
|
||||
def previous_version(self) -> Optional['PopnMusicBase']:
|
||||
def previous_version(self) -> Optional["PopnMusicBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -59,7 +80,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def format_conversion(self, userid: UserID, profile: Profile) -> Node:
|
||||
"""
|
||||
@ -70,9 +91,11 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
format_conversion on that previous version to get the profile to
|
||||
migrate.
|
||||
"""
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -80,7 +103,9 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
"""
|
||||
return oldprofile
|
||||
|
||||
def get_profile_by_refid(self, refid: Optional[str], load_mode: int) -> Optional[Node]:
|
||||
def get_profile_by_refid(
|
||||
self, refid: Optional[str], load_mode: int
|
||||
) -> Optional[Node]:
|
||||
"""
|
||||
Given a RefID, return a formatted profile node. Basically every game
|
||||
needs a profile lookup, even if it handles where that happens in
|
||||
@ -127,7 +152,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
self,
|
||||
refid: Optional[str],
|
||||
name: Optional[str],
|
||||
chara: Optional[int]=None,
|
||||
chara: Optional[int] = None,
|
||||
achievements: Sequence[Achievement] = (),
|
||||
) -> Node:
|
||||
"""
|
||||
@ -138,7 +163,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = 'なし'
|
||||
name = "なし"
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
@ -149,11 +174,11 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
"name": name,
|
||||
},
|
||||
)
|
||||
if chara is not None:
|
||||
profile.replace_int('chara', chara)
|
||||
profile.replace_int("chara", chara)
|
||||
self.put_profile(userid, profile)
|
||||
for achievement in achievements:
|
||||
self.data.local.user.put_achievement(
|
||||
@ -173,8 +198,8 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
chart: int,
|
||||
points: int,
|
||||
medal: int,
|
||||
combo: Optional[int]=None,
|
||||
stats: Optional[Dict[str, int]]=None,
|
||||
combo: Optional[int] = None,
|
||||
stats: Optional[Dict[str, int]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -227,19 +252,19 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Replace medal with highest value
|
||||
scoredata.replace_int('medal', max(scoredata.get_int('medal'), medal))
|
||||
history.replace_int('medal', medal)
|
||||
scoredata.replace_int("medal", max(scoredata.get_int("medal"), medal))
|
||||
history.replace_int("medal", medal)
|
||||
|
||||
if stats is not None:
|
||||
if raised:
|
||||
# We have stats, and there's a new high score, update the stats
|
||||
scoredata.replace_dict('stats', stats)
|
||||
history.replace_dict('stats', stats)
|
||||
scoredata.replace_dict("stats", stats)
|
||||
history.replace_dict("stats", stats)
|
||||
|
||||
if combo is not None:
|
||||
# If we have a combo, replace it
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
history.replace_int("combo", combo)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
@ -290,44 +315,53 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# We saved successfully
|
||||
break
|
||||
|
||||
def broadcast_score(self, userid: UserID, songid: int, chart: int, medal: int, points: int, combo: int, stats: Dict[str, int]) -> None:
|
||||
def broadcast_score(
|
||||
self,
|
||||
userid: UserID,
|
||||
songid: int,
|
||||
chart: int,
|
||||
medal: int,
|
||||
points: int,
|
||||
combo: int,
|
||||
stats: Dict[str, int],
|
||||
) -> None:
|
||||
# Generate scorecard
|
||||
profile = self.get_profile(userid)
|
||||
song = self.data.local.music.get_song(self.game, self.version, songid, chart)
|
||||
|
||||
card_medal = {
|
||||
self.PLAY_MEDAL_CIRCLE_FAILED: 'Failed',
|
||||
self.PLAY_MEDAL_DIAMOND_FAILED: 'Failed',
|
||||
self.PLAY_MEDAL_STAR_FAILED: 'Failed',
|
||||
self.PLAY_MEDAL_EASY_CLEAR: 'Cleared',
|
||||
self.PLAY_MEDAL_CIRCLE_CLEARED: 'Cleared',
|
||||
self.PLAY_MEDAL_DIAMOND_CLEARED: 'Cleared',
|
||||
self.PLAY_MEDAL_STAR_CLEARED: 'Cleared',
|
||||
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: 'Full Combo',
|
||||
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: 'Full Combo',
|
||||
self.PLAY_MEDAL_STAR_FULL_COMBO: 'Full Combo',
|
||||
self.PLAY_MEDAL_PERFECT: 'Perfect',
|
||||
self.PLAY_MEDAL_CIRCLE_FAILED: "Failed",
|
||||
self.PLAY_MEDAL_DIAMOND_FAILED: "Failed",
|
||||
self.PLAY_MEDAL_STAR_FAILED: "Failed",
|
||||
self.PLAY_MEDAL_EASY_CLEAR: "Cleared",
|
||||
self.PLAY_MEDAL_CIRCLE_CLEARED: "Cleared",
|
||||
self.PLAY_MEDAL_DIAMOND_CLEARED: "Cleared",
|
||||
self.PLAY_MEDAL_STAR_CLEARED: "Cleared",
|
||||
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: "Full Combo",
|
||||
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: "Full Combo",
|
||||
self.PLAY_MEDAL_STAR_FULL_COMBO: "Full Combo",
|
||||
self.PLAY_MEDAL_PERFECT: "Perfect",
|
||||
}[medal]
|
||||
|
||||
card_chart = {
|
||||
self.CHART_TYPE_EASY: 'Easy',
|
||||
self.CHART_TYPE_NORMAL: 'Normal',
|
||||
self.CHART_TYPE_HYPER: 'Hyper',
|
||||
self.CHART_TYPE_EX: 'Ex',
|
||||
self.CHART_TYPE_EASY: "Easy",
|
||||
self.CHART_TYPE_NORMAL: "Normal",
|
||||
self.CHART_TYPE_HYPER: "Hyper",
|
||||
self.CHART_TYPE_EX: "Ex",
|
||||
}[chart]
|
||||
|
||||
# Construct the dictionary for the broadcast
|
||||
card_data = {
|
||||
BroadcastConstants.PLAYER_NAME: profile.get_str('name', 'なし'),
|
||||
BroadcastConstants.PLAYER_NAME: profile.get_str("name", "なし"),
|
||||
BroadcastConstants.SONG_NAME: song.name,
|
||||
BroadcastConstants.ARTIST_NAME: song.artist,
|
||||
BroadcastConstants.DIFFICULTY: card_chart,
|
||||
BroadcastConstants.SCORE: str(points),
|
||||
BroadcastConstants.MEDAL: card_medal,
|
||||
BroadcastConstants.COOLS: str(stats['cool']),
|
||||
BroadcastConstants.GREATS: str(stats['great']),
|
||||
BroadcastConstants.GOODS: str(stats['good']),
|
||||
BroadcastConstants.BADS: str(stats['bad']),
|
||||
BroadcastConstants.COOLS: str(stats["cool"]),
|
||||
BroadcastConstants.GREATS: str(stats["great"]),
|
||||
BroadcastConstants.GOODS: str(stats["good"]),
|
||||
BroadcastConstants.BADS: str(stats["bad"]),
|
||||
BroadcastConstants.COMBO: str(combo),
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -66,12 +66,17 @@ class PopnMusicFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['G15', 'H16', 'I17', 'J39', 'K39', 'L39', 'M39']:
|
||||
for gamecode in ["G15", "H16", "I17", "J39", "K39", "L39", "M39"]:
|
||||
Base.register(gamecode, PopnMusicFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date <= 2014061900:
|
||||
return VersionConstants.POPN_MUSIC_SUNNY_PARK
|
||||
@ -87,26 +92,34 @@ class PopnMusicFactory(Factory):
|
||||
return VersionConstants.POPN_MUSIC_KAIMEI_RIDDLES
|
||||
return None
|
||||
|
||||
if model.gamecode == 'G15':
|
||||
if model.gamecode == "G15":
|
||||
return PopnMusicAdventure(data, config, model)
|
||||
if model.gamecode == 'H16':
|
||||
if model.gamecode == "H16":
|
||||
return PopnMusicParty(data, config, model)
|
||||
if model.gamecode == 'I17':
|
||||
if model.gamecode == "I17":
|
||||
return PopnMusicTheMovie(data, config, model)
|
||||
if model.gamecode == 'J39':
|
||||
if model.gamecode == "J39":
|
||||
return PopnMusicSengokuRetsuden(data, config, model)
|
||||
if model.gamecode == 'K39':
|
||||
if model.gamecode == "K39":
|
||||
return PopnMusicTuneStreet(data, config, model)
|
||||
if model.gamecode == 'L39':
|
||||
if model.gamecode == "L39":
|
||||
return PopnMusicFantasia(data, config, model)
|
||||
if model.gamecode == 'M39':
|
||||
if model.gamecode == "M39":
|
||||
if model.version is None:
|
||||
if parentmodel is None:
|
||||
return None
|
||||
|
||||
# We have no way to tell apart newer versions. However, we can make
|
||||
# an educated guess if we happen to be summoned for old profile lookup.
|
||||
if parentmodel.gamecode not in ['G15', 'H16', 'I17', 'J39', 'K39', 'L39', 'M39']:
|
||||
if parentmodel.gamecode not in [
|
||||
"G15",
|
||||
"H16",
|
||||
"I17",
|
||||
"J39",
|
||||
"K39",
|
||||
"L39",
|
||||
"M39",
|
||||
]:
|
||||
return None
|
||||
parentversion = version_from_date(parentmodel.version)
|
||||
if parentversion == VersionConstants.POPN_MUSIC_LAPISTORIA:
|
||||
|
@ -52,31 +52,31 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'ints': [
|
||||
"ints": [
|
||||
{
|
||||
'name': 'Game Phase',
|
||||
'tip': 'Game unlock phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'game_phase',
|
||||
'values': {
|
||||
0: 'NO PHASE',
|
||||
1: 'SECRET DATA RELEASE',
|
||||
2: 'MAX: ALL DATA RELEASE',
|
||||
}
|
||||
"name": "Game Phase",
|
||||
"tip": "Game unlock phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "game_phase",
|
||||
"values": {
|
||||
0: "NO PHASE",
|
||||
1: "SECRET DATA RELEASE",
|
||||
2: "MAX: ALL DATA RELEASE",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'Pop\'n Quest Event Phase',
|
||||
'tip': 'Event phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'event_phase',
|
||||
'values': {
|
||||
0: 'No event',
|
||||
1: 'Phase 1',
|
||||
2: 'Phase 2',
|
||||
3: 'Phase 3',
|
||||
4: 'Phase 4',
|
||||
5: 'Phase MAX',
|
||||
}
|
||||
"name": "Pop'n Quest Event Phase",
|
||||
"tip": "Event phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "event_phase",
|
||||
"values": {
|
||||
0: "No event",
|
||||
1: "Phase 1",
|
||||
2: "Phase 2",
|
||||
3: "Phase 3",
|
||||
4: "Phase 4",
|
||||
5: "Phase MAX",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -94,7 +94,7 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_MEDAL_DIAMOND_FULL_COMBO,
|
||||
self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
|
||||
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
|
||||
}[score.data.get_int('medal')]
|
||||
}[score.data.get_int("medal")]
|
||||
position = {
|
||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
||||
@ -104,67 +104,95 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
return medal << (position * 4)
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('playerdata')
|
||||
root = Node.void("playerdata")
|
||||
|
||||
# Set up the base profile
|
||||
base = Node.void('base')
|
||||
base = Node.void("base")
|
||||
root.add_child(base)
|
||||
base.add_child(Node.string('name', profile.get_str('name', 'なし')))
|
||||
base.add_child(Node.string('g_pm_id', ID.format_extid(profile.extid)))
|
||||
base.add_child(Node.u8('mode', profile.get_int('mode', 0)))
|
||||
base.add_child(Node.s8('button', profile.get_int('button', 0)))
|
||||
base.add_child(Node.s8('last_play_flag', profile.get_int('last_play_flag', -1)))
|
||||
base.add_child(Node.u8('medal_and_friend', profile.get_int('medal_and_friend', 0)))
|
||||
base.add_child(Node.s8('category', profile.get_int('category', -1)))
|
||||
base.add_child(Node.s8('sub_category', profile.get_int('sub_category', -1)))
|
||||
base.add_child(Node.s16('chara', profile.get_int('chara', -1)))
|
||||
base.add_child(Node.s8('chara_category', profile.get_int('chara_category', -1)))
|
||||
base.add_child(Node.u8('collabo', profile.get_int('collabo', 255)))
|
||||
base.add_child(Node.u8('sheet', profile.get_int('sheet', 0)))
|
||||
base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0)))
|
||||
base.add_child(Node.s32('music_open_pt', profile.get_int('music_open_pt', 0)))
|
||||
base.add_child(Node.s8('is_conv', -1))
|
||||
base.add_child(Node.s32('option', profile.get_int('option', 0)))
|
||||
base.add_child(Node.s16('music', profile.get_int('music', -1)))
|
||||
base.add_child(Node.u16('ep', profile.get_int('ep', 0)))
|
||||
base.add_child(Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2)))
|
||||
base.add_child(Node.s32('read_news', profile.get_int('read_news', 0)))
|
||||
base.add_child(Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0)))
|
||||
base.add_child(Node.s8('staff', 0))
|
||||
base.add_child(Node.string("name", profile.get_str("name", "なし")))
|
||||
base.add_child(Node.string("g_pm_id", ID.format_extid(profile.extid)))
|
||||
base.add_child(Node.u8("mode", profile.get_int("mode", 0)))
|
||||
base.add_child(Node.s8("button", profile.get_int("button", 0)))
|
||||
base.add_child(Node.s8("last_play_flag", profile.get_int("last_play_flag", -1)))
|
||||
base.add_child(
|
||||
Node.u8("medal_and_friend", profile.get_int("medal_and_friend", 0))
|
||||
)
|
||||
base.add_child(Node.s8("category", profile.get_int("category", -1)))
|
||||
base.add_child(Node.s8("sub_category", profile.get_int("sub_category", -1)))
|
||||
base.add_child(Node.s16("chara", profile.get_int("chara", -1)))
|
||||
base.add_child(Node.s8("chara_category", profile.get_int("chara_category", -1)))
|
||||
base.add_child(Node.u8("collabo", profile.get_int("collabo", 255)))
|
||||
base.add_child(Node.u8("sheet", profile.get_int("sheet", 0)))
|
||||
base.add_child(Node.s8("tutorial", profile.get_int("tutorial", 0)))
|
||||
base.add_child(Node.s32("music_open_pt", profile.get_int("music_open_pt", 0)))
|
||||
base.add_child(Node.s8("is_conv", -1))
|
||||
base.add_child(Node.s32("option", profile.get_int("option", 0)))
|
||||
base.add_child(Node.s16("music", profile.get_int("music", -1)))
|
||||
base.add_child(Node.u16("ep", profile.get_int("ep", 0)))
|
||||
base.add_child(
|
||||
Node.s32_array("sp_color_flg", profile.get_int_array("sp_color_flg", 2))
|
||||
)
|
||||
base.add_child(Node.s32("read_news", profile.get_int("read_news", 0)))
|
||||
base.add_child(
|
||||
Node.s16(
|
||||
"consecutive_days_coupon", profile.get_int("consecutive_days_coupon", 0)
|
||||
)
|
||||
)
|
||||
base.add_child(Node.s8("staff", 0))
|
||||
|
||||
# Player card section
|
||||
player_card_dict = profile.get_dict('player_card')
|
||||
player_card = Node.void('player_card')
|
||||
player_card_dict = profile.get_dict("player_card")
|
||||
player_card = Node.void("player_card")
|
||||
root.add_child(player_card)
|
||||
player_card.add_child(Node.u8_array('title', player_card_dict.get_int_array('title', 2, [0, 1])))
|
||||
player_card.add_child(Node.u8('frame', player_card_dict.get_int('frame')))
|
||||
player_card.add_child(Node.u8('base', player_card_dict.get_int('base')))
|
||||
player_card.add_child(Node.u8_array('seal', player_card_dict.get_int_array('seal', 2)))
|
||||
player_card.add_child(Node.s32_array('get_title', player_card_dict.get_int_array('get_title', 4)))
|
||||
player_card.add_child(Node.s32('get_frame', player_card_dict.get_int('get_frame')))
|
||||
player_card.add_child(Node.s32('get_base', player_card_dict.get_int('get_base')))
|
||||
player_card.add_child(Node.s32_array('get_seal', player_card_dict.get_int_array('get_seal', 2)))
|
||||
player_card.add_child(Node.s8('is_open', 1))
|
||||
player_card.add_child(
|
||||
Node.u8_array("title", player_card_dict.get_int_array("title", 2, [0, 1]))
|
||||
)
|
||||
player_card.add_child(Node.u8("frame", player_card_dict.get_int("frame")))
|
||||
player_card.add_child(Node.u8("base", player_card_dict.get_int("base")))
|
||||
player_card.add_child(
|
||||
Node.u8_array("seal", player_card_dict.get_int_array("seal", 2))
|
||||
)
|
||||
player_card.add_child(
|
||||
Node.s32_array("get_title", player_card_dict.get_int_array("get_title", 4))
|
||||
)
|
||||
player_card.add_child(
|
||||
Node.s32("get_frame", player_card_dict.get_int("get_frame"))
|
||||
)
|
||||
player_card.add_child(
|
||||
Node.s32("get_base", player_card_dict.get_int("get_base"))
|
||||
)
|
||||
player_card.add_child(
|
||||
Node.s32_array("get_seal", player_card_dict.get_int_array("get_seal", 2))
|
||||
)
|
||||
player_card.add_child(Node.s8("is_open", 1))
|
||||
|
||||
# Player card EX section
|
||||
player_card_ex = Node.void('player_card_ex')
|
||||
player_card_ex = Node.void("player_card_ex")
|
||||
root.add_child(player_card_ex)
|
||||
player_card_ex.add_child(Node.s32('get_title_ex', player_card_dict.get_int('get_title_ex')))
|
||||
player_card_ex.add_child(Node.s32('get_frame_ex', player_card_dict.get_int('get_frame_ex')))
|
||||
player_card_ex.add_child(Node.s32('get_base_ex', player_card_dict.get_int('get_base_ex')))
|
||||
player_card_ex.add_child(Node.s32('get_seal_ex', player_card_dict.get_int('get_seal_ex')))
|
||||
player_card_ex.add_child(
|
||||
Node.s32("get_title_ex", player_card_dict.get_int("get_title_ex"))
|
||||
)
|
||||
player_card_ex.add_child(
|
||||
Node.s32("get_frame_ex", player_card_dict.get_int("get_frame_ex"))
|
||||
)
|
||||
player_card_ex.add_child(
|
||||
Node.s32("get_base_ex", player_card_dict.get_int("get_base_ex"))
|
||||
)
|
||||
player_card_ex.add_child(
|
||||
Node.s32("get_seal_ex", player_card_dict.get_int("get_seal_ex"))
|
||||
)
|
||||
|
||||
# Statistics section and scores section
|
||||
statistics = self.get_play_statistics(userid)
|
||||
base.add_child(Node.s32('total_play_cnt', statistics.total_plays))
|
||||
base.add_child(Node.s16('today_play_cnt', statistics.today_plays))
|
||||
base.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
||||
base.add_child(Node.s32("total_play_cnt", statistics.total_plays))
|
||||
base.add_child(Node.s16("today_play_cnt", statistics.today_plays))
|
||||
base.add_child(Node.s16("consecutive_days", statistics.consecutive_days))
|
||||
|
||||
# Number of rivals that are active for this version.
|
||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||
rivalcount = 0
|
||||
for link in links:
|
||||
if link.type != 'rival':
|
||||
if link.type != "rival":
|
||||
continue
|
||||
|
||||
if not self.has_profile(link.other_userid):
|
||||
@ -172,10 +200,20 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
|
||||
# This profile is valid.
|
||||
rivalcount += 1
|
||||
base.add_child(Node.u8('active_fr_num', rivalcount))
|
||||
base.add_child(Node.u8("active_fr_num", rivalcount))
|
||||
|
||||
last_played = [x[0] for x in self.data.local.music.get_last_played(self.game, self.version, userid, 3)]
|
||||
most_played = [x[0] for x in self.data.local.music.get_most_played(self.game, self.version, userid, 20)]
|
||||
last_played = [
|
||||
x[0]
|
||||
for x in self.data.local.music.get_last_played(
|
||||
self.game, self.version, userid, 3
|
||||
)
|
||||
]
|
||||
most_played = [
|
||||
x[0]
|
||||
for x in self.data.local.music.get_most_played(
|
||||
self.game, self.version, userid, 20
|
||||
)
|
||||
]
|
||||
while len(last_played) < 3:
|
||||
last_played.append(-1)
|
||||
while len(most_played) < 20:
|
||||
@ -198,10 +236,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
self.CHART_TYPE_EX,
|
||||
]:
|
||||
continue
|
||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||
continue
|
||||
|
||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||
clear_medal[score.id] = clear_medal[
|
||||
score.id
|
||||
] | self.__format_medal_for_score(score)
|
||||
hiscore_index = (score.id * 4) + {
|
||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
||||
@ -211,69 +251,81 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||
hiscore_value = score.points << hiscore_bit_pos
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (hiscore_value & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (
|
||||
hiscore_value & 0xFF
|
||||
)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[
|
||||
hiscore_byte_pos + 1
|
||||
] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[
|
||||
hiscore_byte_pos + 2
|
||||
] | ((hiscore_value >> 16) & 0xFF)
|
||||
|
||||
hiscore = bytes(hiscore_array)
|
||||
|
||||
player_card.add_child(Node.s16_array('best_music', most_played[0:3]))
|
||||
base.add_child(Node.s16_array('my_best', most_played))
|
||||
base.add_child(Node.s16_array('latest_music', last_played))
|
||||
base.add_child(Node.u16_array('clear_medal', clear_medal))
|
||||
base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub))
|
||||
player_card.add_child(Node.s16_array("best_music", most_played[0:3]))
|
||||
base.add_child(Node.s16_array("my_best", most_played))
|
||||
base.add_child(Node.s16_array("latest_music", last_played))
|
||||
base.add_child(Node.u16_array("clear_medal", clear_medal))
|
||||
base.add_child(Node.u8_array("clear_medal_sub", clear_medal_sub))
|
||||
|
||||
# Goes outside of base for some reason
|
||||
root.add_child(Node.binary('hiscore', hiscore))
|
||||
root.add_child(Node.binary("hiscore", hiscore))
|
||||
|
||||
# Net VS section
|
||||
netvs = Node.void('netvs')
|
||||
netvs = Node.void("netvs")
|
||||
root.add_child(netvs)
|
||||
netvs.add_child(Node.s32_array('get_ojama', [0, 0]))
|
||||
netvs.add_child(Node.s32('rank_point', 0))
|
||||
netvs.add_child(Node.s32('play_point', 0))
|
||||
netvs.add_child(Node.s16_array('record', [0, 0, 0, 0, 0, 0]))
|
||||
netvs.add_child(Node.u8('rank', 0))
|
||||
netvs.add_child(Node.s8_array('ojama_condition', [0] * 74))
|
||||
netvs.add_child(Node.s8_array('set_ojama', [0, 0, 0]))
|
||||
netvs.add_child(Node.s8_array('set_recommend', [0, 0, 0]))
|
||||
netvs.add_child(Node.s8_array('jewelry', [0] * 15))
|
||||
netvs.add_child(Node.s32_array("get_ojama", [0, 0]))
|
||||
netvs.add_child(Node.s32("rank_point", 0))
|
||||
netvs.add_child(Node.s32("play_point", 0))
|
||||
netvs.add_child(Node.s16_array("record", [0, 0, 0, 0, 0, 0]))
|
||||
netvs.add_child(Node.u8("rank", 0))
|
||||
netvs.add_child(Node.s8_array("ojama_condition", [0] * 74))
|
||||
netvs.add_child(Node.s8_array("set_ojama", [0, 0, 0]))
|
||||
netvs.add_child(Node.s8_array("set_recommend", [0, 0, 0]))
|
||||
netvs.add_child(Node.s8_array("jewelry", [0] * 15))
|
||||
for dialog in [0, 1, 2, 3, 4, 5]:
|
||||
netvs.add_child(Node.string('dialog', f'dialog#{dialog}'))
|
||||
netvs.add_child(Node.string("dialog", f"dialog#{dialog}"))
|
||||
|
||||
sp_data = Node.void('sp_data')
|
||||
sp_data = Node.void("sp_data")
|
||||
root.add_child(sp_data)
|
||||
sp_data.add_child(Node.s32('sp', profile.get_int('sp', 0)))
|
||||
sp_data.add_child(Node.s32("sp", profile.get_int("sp", 0)))
|
||||
|
||||
reflec_data = Node.void('reflec_data')
|
||||
reflec_data = Node.void("reflec_data")
|
||||
root.add_child(reflec_data)
|
||||
reflec_data.add_child(Node.s8_array('reflec', profile.get_int_array('reflec', 2)))
|
||||
reflec_data.add_child(
|
||||
Node.s8_array("reflec", profile.get_int_array("reflec", 2))
|
||||
)
|
||||
|
||||
# Navigate section
|
||||
for i in range(3):
|
||||
navigate_dict = profile.get_dict(f'navigate_{i}')
|
||||
navigate = Node.void('navigate')
|
||||
navigate_dict = profile.get_dict(f"navigate_{i}")
|
||||
navigate = Node.void("navigate")
|
||||
root.add_child(navigate)
|
||||
navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre', -1)))
|
||||
navigate.add_child(Node.s8('image', navigate_dict.get_int('image', -1)))
|
||||
navigate.add_child(Node.s8('level', navigate_dict.get_int('level', -1)))
|
||||
navigate.add_child(Node.s8('ojama', navigate_dict.get_int('ojama', -1)))
|
||||
navigate.add_child(Node.s16('limit_num', navigate_dict.get_int('limit_num', -1)))
|
||||
navigate.add_child(Node.s8('button', navigate_dict.get_int('button', -1)))
|
||||
navigate.add_child(Node.s8('life', navigate_dict.get_int('life', -1)))
|
||||
navigate.add_child(Node.s16('progress', navigate_dict.get_int('progress', -1)))
|
||||
navigate.add_child(Node.s8("genre", navigate_dict.get_int("genre", -1)))
|
||||
navigate.add_child(Node.s8("image", navigate_dict.get_int("image", -1)))
|
||||
navigate.add_child(Node.s8("level", navigate_dict.get_int("level", -1)))
|
||||
navigate.add_child(Node.s8("ojama", navigate_dict.get_int("ojama", -1)))
|
||||
navigate.add_child(
|
||||
Node.s16("limit_num", navigate_dict.get_int("limit_num", -1))
|
||||
)
|
||||
navigate.add_child(Node.s8("button", navigate_dict.get_int("button", -1)))
|
||||
navigate.add_child(Node.s8("life", navigate_dict.get_int("life", -1)))
|
||||
navigate.add_child(
|
||||
Node.s16("progress", navigate_dict.get_int("progress", -1))
|
||||
)
|
||||
|
||||
return root
|
||||
|
||||
def format_conversion(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('playerdata')
|
||||
root = Node.void("playerdata")
|
||||
|
||||
root.add_child(Node.string('name', profile.get_str('name', 'なし')))
|
||||
root.add_child(Node.s16('chara', profile.get_int('chara', -1)))
|
||||
root.add_child(Node.s32('option', profile.get_int('option', 0)))
|
||||
root.add_child(Node.u8('version', 0))
|
||||
root.add_child(Node.u8('kind', 0))
|
||||
root.add_child(Node.u8('season', 0))
|
||||
root.add_child(Node.string("name", profile.get_str("name", "なし")))
|
||||
root.add_child(Node.s16("chara", profile.get_int("chara", -1)))
|
||||
root.add_child(Node.s32("option", profile.get_int("option", 0)))
|
||||
root.add_child(Node.u8("version", 0))
|
||||
root.add_child(Node.u8("kind", 0))
|
||||
root.add_child(Node.u8("season", 0))
|
||||
|
||||
clear_medal = [0] * self.GAME_MAX_MUSIC_ID
|
||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
||||
@ -291,10 +343,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
self.CHART_TYPE_EX,
|
||||
]:
|
||||
continue
|
||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||
continue
|
||||
|
||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||
clear_medal[score.id] = clear_medal[
|
||||
score.id
|
||||
] | self.__format_medal_for_score(score)
|
||||
hiscore_index = (score.id * 4) + {
|
||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
||||
@ -304,86 +358,114 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||
hiscore_value = score.points << hiscore_bit_pos
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (hiscore_value & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (
|
||||
hiscore_value & 0xFF
|
||||
)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[
|
||||
hiscore_byte_pos + 1
|
||||
] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[
|
||||
hiscore_byte_pos + 2
|
||||
] | ((hiscore_value >> 16) & 0xFF)
|
||||
|
||||
root.add_child(Node.u16_array('clear_medal', clear_medal))
|
||||
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
|
||||
root.add_child(Node.u16_array("clear_medal", clear_medal))
|
||||
root.add_child(Node.binary("hiscore", bytes(hiscore_array)))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
# For some reason, Pop'n 20 sends us two profile saves, one with 'not done yet'
|
||||
# so we only want to process the done yet node. The 'not gameover' save has
|
||||
# jubeat collabo stuff set in it, but we don't use that so it doesn't matter.
|
||||
if request.child_value('is_not_gameover') == 1:
|
||||
if request.child_value("is_not_gameover") == 1:
|
||||
return oldprofile
|
||||
|
||||
newprofile = oldprofile.clone()
|
||||
newprofile.replace_int('option', request.child_value('option'))
|
||||
newprofile.replace_int('chara', request.child_value('chara'))
|
||||
newprofile.replace_int('mode', request.child_value('mode'))
|
||||
newprofile.replace_int('button', request.child_value('button'))
|
||||
newprofile.replace_int('music', request.child_value('music'))
|
||||
newprofile.replace_int('sheet', request.child_value('sheet'))
|
||||
newprofile.replace_int('last_play_flag', request.child_value('last_play_flag'))
|
||||
newprofile.replace_int('category', request.child_value('category'))
|
||||
newprofile.replace_int('sub_category', request.child_value('sub_category'))
|
||||
newprofile.replace_int('chara_category', request.child_value('chara_category'))
|
||||
newprofile.replace_int('medal_and_friend', request.child_value('medal_and_friend'))
|
||||
newprofile.replace_int('ep', request.child_value('ep'))
|
||||
newprofile.replace_int_array('sp_color_flg', 2, request.child_value('sp_color_flg'))
|
||||
newprofile.replace_int('read_news', request.child_value('read_news'))
|
||||
newprofile.replace_int('consecutive_days_coupon', request.child_value('consecutive_days_coupon'))
|
||||
newprofile.replace_int('tutorial', request.child_value('tutorial'))
|
||||
newprofile.replace_int('music_open_pt', request.child_value('music_open_pt'))
|
||||
newprofile.replace_int('collabo', request.child_value('collabo'))
|
||||
newprofile.replace_int("option", request.child_value("option"))
|
||||
newprofile.replace_int("chara", request.child_value("chara"))
|
||||
newprofile.replace_int("mode", request.child_value("mode"))
|
||||
newprofile.replace_int("button", request.child_value("button"))
|
||||
newprofile.replace_int("music", request.child_value("music"))
|
||||
newprofile.replace_int("sheet", request.child_value("sheet"))
|
||||
newprofile.replace_int("last_play_flag", request.child_value("last_play_flag"))
|
||||
newprofile.replace_int("category", request.child_value("category"))
|
||||
newprofile.replace_int("sub_category", request.child_value("sub_category"))
|
||||
newprofile.replace_int("chara_category", request.child_value("chara_category"))
|
||||
newprofile.replace_int(
|
||||
"medal_and_friend", request.child_value("medal_and_friend")
|
||||
)
|
||||
newprofile.replace_int("ep", request.child_value("ep"))
|
||||
newprofile.replace_int_array(
|
||||
"sp_color_flg", 2, request.child_value("sp_color_flg")
|
||||
)
|
||||
newprofile.replace_int("read_news", request.child_value("read_news"))
|
||||
newprofile.replace_int(
|
||||
"consecutive_days_coupon", request.child_value("consecutive_days_coupon")
|
||||
)
|
||||
newprofile.replace_int("tutorial", request.child_value("tutorial"))
|
||||
newprofile.replace_int("music_open_pt", request.child_value("music_open_pt"))
|
||||
newprofile.replace_int("collabo", request.child_value("collabo"))
|
||||
|
||||
sp_node = request.child('sp_data')
|
||||
sp_node = request.child("sp_data")
|
||||
if sp_node is not None:
|
||||
newprofile.replace_int('sp', sp_node.child_value('sp'))
|
||||
newprofile.replace_int("sp", sp_node.child_value("sp"))
|
||||
|
||||
reflec_node = request.child('reflec_data')
|
||||
reflec_node = request.child("reflec_data")
|
||||
if reflec_node is not None:
|
||||
newprofile.replace_int_array('reflec', 2, reflec_node.child_value('reflec'))
|
||||
newprofile.replace_int_array("reflec", 2, reflec_node.child_value("reflec"))
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid)
|
||||
|
||||
# Extract player card stuff
|
||||
player_card_dict = newprofile.get_dict('player_card')
|
||||
player_card_dict.replace_int_array('title', 2, request.child_value('title'))
|
||||
player_card_dict.replace_int('frame', request.child_value('frame'))
|
||||
player_card_dict.replace_int('base', request.child_value('base'))
|
||||
player_card_dict.replace_int_array('seal', 2, request.child_value('seal'))
|
||||
player_card_dict.replace_int_array('get_title', 4, request.child_value('get_title'))
|
||||
player_card_dict.replace_int('get_frame', request.child_value('get_frame'))
|
||||
player_card_dict.replace_int('get_base', request.child_value('get_base'))
|
||||
player_card_dict.replace_int_array('get_seal', 2, request.child_value('get_seal'))
|
||||
player_card_dict = newprofile.get_dict("player_card")
|
||||
player_card_dict.replace_int_array("title", 2, request.child_value("title"))
|
||||
player_card_dict.replace_int("frame", request.child_value("frame"))
|
||||
player_card_dict.replace_int("base", request.child_value("base"))
|
||||
player_card_dict.replace_int_array("seal", 2, request.child_value("seal"))
|
||||
player_card_dict.replace_int_array(
|
||||
"get_title", 4, request.child_value("get_title")
|
||||
)
|
||||
player_card_dict.replace_int("get_frame", request.child_value("get_frame"))
|
||||
player_card_dict.replace_int("get_base", request.child_value("get_base"))
|
||||
player_card_dict.replace_int_array(
|
||||
"get_seal", 2, request.child_value("get_seal")
|
||||
)
|
||||
|
||||
player_card_ex = request.child('player_card_ex')
|
||||
player_card_ex = request.child("player_card_ex")
|
||||
if player_card_ex is not None:
|
||||
player_card_dict.replace_int('get_title_ex', player_card_ex.child_value('get_title_ex'))
|
||||
player_card_dict.replace_int('get_frame_ex', player_card_ex.child_value('get_frame_ex'))
|
||||
player_card_dict.replace_int('get_base_ex', player_card_ex.child_value('get_base_ex'))
|
||||
player_card_dict.replace_int('get_seal_ex', player_card_ex.child_value('get_seal_ex'))
|
||||
newprofile.replace_dict('player_card', player_card_dict)
|
||||
player_card_dict.replace_int(
|
||||
"get_title_ex", player_card_ex.child_value("get_title_ex")
|
||||
)
|
||||
player_card_dict.replace_int(
|
||||
"get_frame_ex", player_card_ex.child_value("get_frame_ex")
|
||||
)
|
||||
player_card_dict.replace_int(
|
||||
"get_base_ex", player_card_ex.child_value("get_base_ex")
|
||||
)
|
||||
player_card_dict.replace_int(
|
||||
"get_seal_ex", player_card_ex.child_value("get_seal_ex")
|
||||
)
|
||||
newprofile.replace_dict("player_card", player_card_dict)
|
||||
|
||||
# Extract navigate stuff
|
||||
nav_id = 0
|
||||
for navigate in request.children:
|
||||
if navigate.name == 'navigate':
|
||||
navigate_dict = newprofile.get_dict(f'navigate_{nav_id}')
|
||||
navigate_dict.replace_int('genre', navigate.child_value('genre'))
|
||||
navigate_dict.replace_int('image', navigate.child_value('image'))
|
||||
navigate_dict.replace_int('level', navigate.child_value('level'))
|
||||
navigate_dict.replace_int('ojama', navigate.child_value('ojama'))
|
||||
navigate_dict.replace_int('limit_num', navigate.child_value('limit_num'))
|
||||
navigate_dict.replace_int('button', navigate.child_value('button'))
|
||||
navigate_dict.replace_int('life', navigate.child_value('life'))
|
||||
navigate_dict.replace_int('progress', navigate.child_value('progress'))
|
||||
newprofile.replace_dict(f'navigate_{nav_id}', navigate_dict)
|
||||
if navigate.name == "navigate":
|
||||
navigate_dict = newprofile.get_dict(f"navigate_{nav_id}")
|
||||
navigate_dict.replace_int("genre", navigate.child_value("genre"))
|
||||
navigate_dict.replace_int("image", navigate.child_value("image"))
|
||||
navigate_dict.replace_int("level", navigate.child_value("level"))
|
||||
navigate_dict.replace_int("ojama", navigate.child_value("ojama"))
|
||||
navigate_dict.replace_int(
|
||||
"limit_num", navigate.child_value("limit_num")
|
||||
)
|
||||
navigate_dict.replace_int("button", navigate.child_value("button"))
|
||||
navigate_dict.replace_int("life", navigate.child_value("life"))
|
||||
navigate_dict.replace_int("progress", navigate.child_value("progress"))
|
||||
newprofile.replace_dict(f"navigate_{nav_id}", navigate_dict)
|
||||
nav_id += 1
|
||||
|
||||
if nav_id >= 3:
|
||||
@ -391,15 +473,15 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
|
||||
# Extract scores
|
||||
for node in request.children:
|
||||
if node.name == 'stage':
|
||||
songid = node.child_value('no')
|
||||
if node.name == "stage":
|
||||
songid = node.child_value("no")
|
||||
chart = {
|
||||
self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
|
||||
self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
|
||||
self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
|
||||
self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
|
||||
}[node.child_value('sheet')]
|
||||
medal = (node.child_value('n_data') >> (chart * 4)) & 0x000F
|
||||
}[node.child_value("sheet")]
|
||||
medal = (node.child_value("n_data") >> (chart * 4)) & 0x000F
|
||||
medal = {
|
||||
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
|
||||
self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_FAILED,
|
||||
@ -412,116 +494,132 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
self.GAME_PLAY_MEDAL_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO,
|
||||
self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
|
||||
}[medal]
|
||||
points = node.child_value('score')
|
||||
points = node.child_value("score")
|
||||
self.update_score(userid, songid, chart, points, medal)
|
||||
|
||||
return newprofile
|
||||
|
||||
def handle_playerdata_expire_request(self, request: Node) -> Node:
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def handle_playerdata_logout_request(self, request: Node) -> Node:
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def handle_playerdata_get_request(self, request: Node) -> Node:
|
||||
modelstring = request.attribute('model')
|
||||
refid = request.child_value('ref_id')
|
||||
modelstring = request.attribute("model")
|
||||
refid = request.child_value("ref_id")
|
||||
root = self.get_profile_by_refid(
|
||||
refid,
|
||||
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
||||
)
|
||||
if root is None:
|
||||
root = Node.void('playerdata')
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root = Node.void("playerdata")
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
def handle_playerdata_conversion_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('ref_id')
|
||||
name = request.child_value('name')
|
||||
chara = request.child_value('chara')
|
||||
refid = request.child_value("ref_id")
|
||||
name = request.child_value("name")
|
||||
chara = request.child_value("chara")
|
||||
root = self.new_profile_by_refid(refid, name, chara)
|
||||
if root is None:
|
||||
root = Node.void('playerdata')
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root = Node.void("playerdata")
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
def handle_playerdata_new_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('ref_id')
|
||||
name = request.child_value('name')
|
||||
refid = request.child_value("ref_id")
|
||||
name = request.child_value("name")
|
||||
root = self.new_profile_by_refid(refid, name)
|
||||
if root is None:
|
||||
root = Node.void('playerdata')
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root = Node.void("playerdata")
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
def handle_playerdata_set_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('ref_id')
|
||||
refid = request.attribute("ref_id")
|
||||
machine = self.get_machine()
|
||||
|
||||
root = Node.void('playerdata')
|
||||
root.add_child(Node.s8('pref', machine.data.get_int('pref', self.get_machine_region())))
|
||||
root = Node.void("playerdata")
|
||||
root.add_child(
|
||||
Node.s8("pref", machine.data.get_int("pref", self.get_machine_region()))
|
||||
)
|
||||
|
||||
if refid is None:
|
||||
root.add_child(Node.string('name', ''))
|
||||
root.add_child(Node.s16('chara', -1))
|
||||
root.add_child(Node.u8('frame', 0))
|
||||
root.add_child(Node.u8('base', 0))
|
||||
root.add_child(Node.u8('seal_1', 0))
|
||||
root.add_child(Node.u8('seal_2', 0))
|
||||
root.add_child(Node.u8('title_1', 0))
|
||||
root.add_child(Node.u8('title_2', 0))
|
||||
root.add_child(Node.s16('recommend_1', -1))
|
||||
root.add_child(Node.s16('recommend_2', -1))
|
||||
root.add_child(Node.s16('recommend_3', -1))
|
||||
root.add_child(Node.string('message', ''))
|
||||
root.add_child(Node.string("name", ""))
|
||||
root.add_child(Node.s16("chara", -1))
|
||||
root.add_child(Node.u8("frame", 0))
|
||||
root.add_child(Node.u8("base", 0))
|
||||
root.add_child(Node.u8("seal_1", 0))
|
||||
root.add_child(Node.u8("seal_2", 0))
|
||||
root.add_child(Node.u8("title_1", 0))
|
||||
root.add_child(Node.u8("title_2", 0))
|
||||
root.add_child(Node.s16("recommend_1", -1))
|
||||
root.add_child(Node.s16("recommend_2", -1))
|
||||
root.add_child(Node.s16("recommend_3", -1))
|
||||
root.add_child(Node.string("message", ""))
|
||||
return root
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
root.add_child(Node.string('name', ''))
|
||||
root.add_child(Node.s16('chara', -1))
|
||||
root.add_child(Node.u8('frame', 0))
|
||||
root.add_child(Node.u8('base', 0))
|
||||
root.add_child(Node.u8('seal_1', 0))
|
||||
root.add_child(Node.u8('seal_2', 0))
|
||||
root.add_child(Node.u8('title_1', 0))
|
||||
root.add_child(Node.u8('title_2', 0))
|
||||
root.add_child(Node.s16('recommend_1', -1))
|
||||
root.add_child(Node.s16('recommend_2', -1))
|
||||
root.add_child(Node.s16('recommend_3', -1))
|
||||
root.add_child(Node.string('message', ''))
|
||||
root.add_child(Node.string("name", ""))
|
||||
root.add_child(Node.s16("chara", -1))
|
||||
root.add_child(Node.u8("frame", 0))
|
||||
root.add_child(Node.u8("base", 0))
|
||||
root.add_child(Node.u8("seal_1", 0))
|
||||
root.add_child(Node.u8("seal_2", 0))
|
||||
root.add_child(Node.u8("title_1", 0))
|
||||
root.add_child(Node.u8("title_2", 0))
|
||||
root.add_child(Node.s16("recommend_1", -1))
|
||||
root.add_child(Node.s16("recommend_2", -1))
|
||||
root.add_child(Node.s16("recommend_3", -1))
|
||||
root.add_child(Node.string("message", ""))
|
||||
return root
|
||||
|
||||
oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
||||
oldprofile = self.get_profile(userid) or Profile(
|
||||
self.game, self.version, refid, 0
|
||||
)
|
||||
newprofile = self.unformat_profile(userid, request, oldprofile)
|
||||
|
||||
if newprofile is not None:
|
||||
player_card_dict = newprofile.get_dict('player_card')
|
||||
player_card_dict = newprofile.get_dict("player_card")
|
||||
|
||||
self.put_profile(userid, newprofile)
|
||||
root.add_child(Node.string('name', newprofile.get_str('name', 'なし')))
|
||||
root.add_child(Node.s16('chara', newprofile.get_int('chara', -1)))
|
||||
root.add_child(Node.u8('frame', player_card_dict.get_int('frame')))
|
||||
root.add_child(Node.u8('base', player_card_dict.get_int('base')))
|
||||
root.add_child(Node.u8('seal_1', player_card_dict.get_int_array('seal', 2)[0]))
|
||||
root.add_child(Node.u8('seal_2', player_card_dict.get_int_array('seal', 2)[1]))
|
||||
root.add_child(Node.u8('title_1', player_card_dict.get_int_array('title', 2, [0, 1])[0]))
|
||||
root.add_child(Node.u8('title_2', player_card_dict.get_int_array('title', 2, [0, 1])[1]))
|
||||
root.add_child(Node.s16('recommend_1', -1))
|
||||
root.add_child(Node.s16('recommend_2', -1))
|
||||
root.add_child(Node.s16('recommend_3', -1))
|
||||
root.add_child(Node.string('message', ''))
|
||||
root.add_child(Node.string("name", newprofile.get_str("name", "なし")))
|
||||
root.add_child(Node.s16("chara", newprofile.get_int("chara", -1)))
|
||||
root.add_child(Node.u8("frame", player_card_dict.get_int("frame")))
|
||||
root.add_child(Node.u8("base", player_card_dict.get_int("base")))
|
||||
root.add_child(
|
||||
Node.u8("seal_1", player_card_dict.get_int_array("seal", 2)[0])
|
||||
)
|
||||
root.add_child(
|
||||
Node.u8("seal_2", player_card_dict.get_int_array("seal", 2)[1])
|
||||
)
|
||||
root.add_child(
|
||||
Node.u8(
|
||||
"title_1", player_card_dict.get_int_array("title", 2, [0, 1])[0]
|
||||
)
|
||||
)
|
||||
root.add_child(
|
||||
Node.u8(
|
||||
"title_2", player_card_dict.get_int_array("title", 2, [0, 1])[1]
|
||||
)
|
||||
)
|
||||
root.add_child(Node.s16("recommend_1", -1))
|
||||
root.add_child(Node.s16("recommend_2", -1))
|
||||
root.add_child(Node.s16("recommend_3", -1))
|
||||
root.add_child(Node.string("message", ""))
|
||||
|
||||
return root
|
||||
|
||||
def handle_playerdata_friend_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('ref_id')
|
||||
root = Node.void('playerdata')
|
||||
refid = request.attribute("ref_id")
|
||||
root = Node.void("playerdata")
|
||||
|
||||
# Look up our own user ID based on the RefID provided.
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
# Grab the links that we care about.
|
||||
@ -529,7 +627,7 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
profiles: Dict[UserID, Profile] = {}
|
||||
rivals: List[Link] = []
|
||||
for link in links:
|
||||
if link.type != 'rival':
|
||||
if link.type != "rival":
|
||||
continue
|
||||
|
||||
other_profile = self.get_profile(link.other_userid)
|
||||
@ -544,17 +642,19 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
|
||||
|
||||
# First, output general profile info.
|
||||
friend = Node.void('friend')
|
||||
friend = Node.void("friend")
|
||||
root.add_child(friend)
|
||||
|
||||
# This might be for having non-active or non-confirmed friends, but setting to 0 makes the
|
||||
# ranking numbers disappear and the player icon show a questionmark.
|
||||
friend.add_child(Node.s8('open', 1))
|
||||
friend.add_child(Node.s8("open", 1))
|
||||
|
||||
# Set up some sane defaults.
|
||||
friend.add_child(Node.string('name', rivalprofile.get_str('name', 'なし')))
|
||||
friend.add_child(Node.string('g_pm_id', ID.format_extid(rivalprofile.extid)))
|
||||
friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1)))
|
||||
friend.add_child(Node.string("name", rivalprofile.get_str("name", "なし")))
|
||||
friend.add_child(
|
||||
Node.string("g_pm_id", ID.format_extid(rivalprofile.extid))
|
||||
)
|
||||
friend.add_child(Node.s16("chara", rivalprofile.get_int("chara", -1)))
|
||||
|
||||
# Perform hiscore/medal conversion.
|
||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
||||
@ -571,10 +671,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
self.CHART_TYPE_EX,
|
||||
]:
|
||||
continue
|
||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||
continue
|
||||
|
||||
clear_medal[score.id] = clear_medal[score.id] | self.__format_medal_for_score(score)
|
||||
clear_medal[score.id] = clear_medal[
|
||||
score.id
|
||||
] | self.__format_medal_for_score(score)
|
||||
hiscore_index = (score.id * 4) + {
|
||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
||||
@ -584,13 +686,19 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||
hiscore_value = score.points << hiscore_bit_pos
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (hiscore_value & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (
|
||||
hiscore_value & 0xFF
|
||||
)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[
|
||||
hiscore_byte_pos + 1
|
||||
] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[
|
||||
hiscore_byte_pos + 2
|
||||
] | ((hiscore_value >> 16) & 0xFF)
|
||||
|
||||
hiscore = bytes(hiscore_array)
|
||||
friend.add_child(Node.u16_array('clear_medal', clear_medal))
|
||||
friend.add_child(Node.binary('hiscore', hiscore))
|
||||
friend.add_child(Node.u16_array("clear_medal", clear_medal))
|
||||
friend.add_child(Node.binary("hiscore", hiscore))
|
||||
|
||||
# Note that if we ever support internet ranking mode, there's an 'ir_hiscore' node here as well.
|
||||
|
||||
@ -601,41 +709,45 @@ class PopnMusicFantasia(PopnMusicBase):
|
||||
|
||||
def handle_game_get_request(self, request: Node) -> Node:
|
||||
game_config = self.get_game_config()
|
||||
game_phase = game_config.get_int('game_phase')
|
||||
event_phase = game_config.get_int('event_phase')
|
||||
game_phase = game_config.get_int("game_phase")
|
||||
event_phase = game_config.get_int("event_phase")
|
||||
|
||||
root = Node.void('game')
|
||||
root.add_child(Node.s32('game_phase', game_phase))
|
||||
root.add_child(Node.s32('ir_phase', 0))
|
||||
root.add_child(Node.s32('event_phase', event_phase))
|
||||
root.add_child(Node.s32('netvs_phase', 0)) # Net taisen mode, we don't support lobbies.
|
||||
root.add_child(Node.s32('card_phase', 6))
|
||||
root.add_child(Node.s32('illust_phase', 2))
|
||||
root.add_child(Node.s32('psp_phase', 5)) # Unlock songs from Pop'n Music Portable.
|
||||
root.add_child(Node.s32('other_phase', 1))
|
||||
root.add_child(Node.s32('jubeat_phase', 1))
|
||||
root.add_child(Node.s32('public_phase', 3))
|
||||
root.add_child(Node.s32('kac_phase', 2))
|
||||
root.add_child(Node.s32('local_matching_enable', 1))
|
||||
root.add_child(Node.s32('n_matching_sec', 60))
|
||||
root.add_child(Node.s32('l_matching_sec', 60))
|
||||
root.add_child(Node.s32('is_check_cpu', 0))
|
||||
root.add_child(Node.s32('week_no', 0))
|
||||
root.add_child(Node.s32('team_day', 0))
|
||||
root.add_child(Node.s32_array('ng_illust', [-1] * 64))
|
||||
root.add_child(Node.s16_array('sel_ranking', [-1] * 10))
|
||||
root.add_child(Node.s16_array('up_ranking', [-1] * 10))
|
||||
root = Node.void("game")
|
||||
root.add_child(Node.s32("game_phase", game_phase))
|
||||
root.add_child(Node.s32("ir_phase", 0))
|
||||
root.add_child(Node.s32("event_phase", event_phase))
|
||||
root.add_child(
|
||||
Node.s32("netvs_phase", 0)
|
||||
) # Net taisen mode, we don't support lobbies.
|
||||
root.add_child(Node.s32("card_phase", 6))
|
||||
root.add_child(Node.s32("illust_phase", 2))
|
||||
root.add_child(
|
||||
Node.s32("psp_phase", 5)
|
||||
) # Unlock songs from Pop'n Music Portable.
|
||||
root.add_child(Node.s32("other_phase", 1))
|
||||
root.add_child(Node.s32("jubeat_phase", 1))
|
||||
root.add_child(Node.s32("public_phase", 3))
|
||||
root.add_child(Node.s32("kac_phase", 2))
|
||||
root.add_child(Node.s32("local_matching_enable", 1))
|
||||
root.add_child(Node.s32("n_matching_sec", 60))
|
||||
root.add_child(Node.s32("l_matching_sec", 60))
|
||||
root.add_child(Node.s32("is_check_cpu", 0))
|
||||
root.add_child(Node.s32("week_no", 0))
|
||||
root.add_child(Node.s32("team_day", 0))
|
||||
root.add_child(Node.s32_array("ng_illust", [-1] * 64))
|
||||
root.add_child(Node.s16_array("sel_ranking", [-1] * 10))
|
||||
root.add_child(Node.s16_array("up_ranking", [-1] * 10))
|
||||
|
||||
return root
|
||||
|
||||
def handle_game_active_request(self, request: Node) -> Node:
|
||||
# Update the name of this cab for admin purposes. Also store the prefecture.
|
||||
machine = self.get_machine()
|
||||
machine.name = request.child_value('shop_name') or machine.name
|
||||
machine.data.replace_int('pref', request.child_value('pref'))
|
||||
machine.name = request.child_value("shop_name") or machine.name
|
||||
machine.data.replace_int("pref", request.child_value("pref"))
|
||||
self.update_machine(machine)
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_lobby_requests(self, request: Node) -> Node:
|
||||
# Stub out the entire lobby service
|
||||
return Node.void('lobby')
|
||||
return Node.void("lobby")
|
||||
|
@ -41,164 +41,164 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'ints': [
|
||||
"ints": [
|
||||
{
|
||||
'name': 'Music Open Phase',
|
||||
'tip': 'Default music phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'music_phase',
|
||||
'values': {
|
||||
"name": "Music Open Phase",
|
||||
"tip": "Default music phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "music_phase",
|
||||
"values": {
|
||||
# The value goes to 30 now, but it starts where usaneko left off at 23
|
||||
# Unlocks a total of 10 songs
|
||||
23: 'No music unlocks',
|
||||
24: 'Phase 1',
|
||||
25: 'Phase 2',
|
||||
26: 'Phase 3',
|
||||
27: 'Phase 4',
|
||||
28: 'Phase 5',
|
||||
29: 'Phase 6',
|
||||
30: 'Phase MAX',
|
||||
}
|
||||
23: "No music unlocks",
|
||||
24: "Phase 1",
|
||||
25: "Phase 2",
|
||||
26: "Phase 3",
|
||||
27: "Phase 4",
|
||||
28: "Phase 5",
|
||||
29: "Phase 6",
|
||||
30: "Phase MAX",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'Kaimei! MN tanteisha event Phase',
|
||||
'tip': 'Kaimei! MN tanteisha event phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'mn_tanteisha_phase',
|
||||
'values': {
|
||||
0: 'Disabled',
|
||||
1: 'Roki',
|
||||
2: 'shiroro',
|
||||
3: 'PIERRE&JILL',
|
||||
4: 'ROSA',
|
||||
5: 'taoxiang',
|
||||
6: 'TangTang',
|
||||
7: 'OTOBEAR',
|
||||
8: 'kaorin',
|
||||
9: 'CHARLY',
|
||||
10: 'ALOE',
|
||||
11: 'RIE♥chan',
|
||||
12: 'hina',
|
||||
13: 'PAPYRUS',
|
||||
14: '雷蔵, miho, RIE♥chan, Ryusei Honey',
|
||||
15: 'Murasaki',
|
||||
16: 'Lucifelle',
|
||||
17: '六',
|
||||
18: 'stella',
|
||||
19: 'ちせ',
|
||||
20: 'LISA',
|
||||
21: 'SUMIRE',
|
||||
22: 'SHISHITUGU',
|
||||
23: 'WALKER',
|
||||
24: 'Candy',
|
||||
25: 'Jade',
|
||||
26: 'AYA',
|
||||
27: 'kaorin',
|
||||
28: 'Lord Meh',
|
||||
29: 'HAMANOV',
|
||||
30: 'Agent',
|
||||
31: 'Yima',
|
||||
32: 'ikkei',
|
||||
33: 'echidna',
|
||||
34: 'lithos',
|
||||
35: 'SMOKE',
|
||||
36: 'the KING',
|
||||
37: 'Kicoro',
|
||||
38: 'DEBORAH',
|
||||
39: 'Teruo',
|
||||
40: 'the TOWER',
|
||||
41: 'Mamoru-kun',
|
||||
42: 'Canopus',
|
||||
43: 'Mimi Nyami',
|
||||
44: 'iO-LOWER',
|
||||
45: 'BOY',
|
||||
46: 'Sergei',
|
||||
47: 'SAPPHIRE',
|
||||
48: 'Chocky',
|
||||
49: 'HAPPPY',
|
||||
50: 'SHOLLKEE',
|
||||
51: 'CHARA-O',
|
||||
52: 'Hugh, GRIM, SUMIKO',
|
||||
53: 'Peetan',
|
||||
54: 'SHARK',
|
||||
55: 'Nakajima-san',
|
||||
56: 'KIKYO',
|
||||
57: 'SUMIRE',
|
||||
58: 'NAKAJI',
|
||||
59: 'moi moi',
|
||||
60: 'TITICACA',
|
||||
61: 'MASAMUNE',
|
||||
62: 'YUMMY'
|
||||
"name": "Kaimei! MN tanteisha event Phase",
|
||||
"tip": "Kaimei! MN tanteisha event phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "mn_tanteisha_phase",
|
||||
"values": {
|
||||
0: "Disabled",
|
||||
1: "Roki",
|
||||
2: "shiroro",
|
||||
3: "PIERRE&JILL",
|
||||
4: "ROSA",
|
||||
5: "taoxiang",
|
||||
6: "TangTang",
|
||||
7: "OTOBEAR",
|
||||
8: "kaorin",
|
||||
9: "CHARLY",
|
||||
10: "ALOE",
|
||||
11: "RIE♥chan",
|
||||
12: "hina",
|
||||
13: "PAPYRUS",
|
||||
14: "雷蔵, miho, RIE♥chan, Ryusei Honey",
|
||||
15: "Murasaki",
|
||||
16: "Lucifelle",
|
||||
17: "六",
|
||||
18: "stella",
|
||||
19: "ちせ",
|
||||
20: "LISA",
|
||||
21: "SUMIRE",
|
||||
22: "SHISHITUGU",
|
||||
23: "WALKER",
|
||||
24: "Candy",
|
||||
25: "Jade",
|
||||
26: "AYA",
|
||||
27: "kaorin",
|
||||
28: "Lord Meh",
|
||||
29: "HAMANOV",
|
||||
30: "Agent",
|
||||
31: "Yima",
|
||||
32: "ikkei",
|
||||
33: "echidna",
|
||||
34: "lithos",
|
||||
35: "SMOKE",
|
||||
36: "the KING",
|
||||
37: "Kicoro",
|
||||
38: "DEBORAH",
|
||||
39: "Teruo",
|
||||
40: "the TOWER",
|
||||
41: "Mamoru-kun",
|
||||
42: "Canopus",
|
||||
43: "Mimi Nyami",
|
||||
44: "iO-LOWER",
|
||||
45: "BOY",
|
||||
46: "Sergei",
|
||||
47: "SAPPHIRE",
|
||||
48: "Chocky",
|
||||
49: "HAPPPY",
|
||||
50: "SHOLLKEE",
|
||||
51: "CHARA-O",
|
||||
52: "Hugh, GRIM, SUMIKO",
|
||||
53: "Peetan",
|
||||
54: "SHARK",
|
||||
55: "Nakajima-san",
|
||||
56: "KIKYO",
|
||||
57: "SUMIRE",
|
||||
58: "NAKAJI",
|
||||
59: "moi moi",
|
||||
60: "TITICACA",
|
||||
61: "MASAMUNE",
|
||||
62: "YUMMY",
|
||||
},
|
||||
},
|
||||
{
|
||||
# For festive times, it's possible to change the welcome greeting. I'm not sure why you would want to change this, but now you can.
|
||||
'name': 'Holiday Greeting',
|
||||
'tip': 'Changes the payment selection confirmation sound.',
|
||||
'category': 'game_config',
|
||||
'setting': 'holiday_greeting',
|
||||
'values': {
|
||||
0: 'Okay!',
|
||||
1: 'Merry Christmas!',
|
||||
2: 'Happy New Year!',
|
||||
}
|
||||
"name": "Holiday Greeting",
|
||||
"tip": "Changes the payment selection confirmation sound.",
|
||||
"category": "game_config",
|
||||
"setting": "holiday_greeting",
|
||||
"values": {
|
||||
0: "Okay!",
|
||||
1: "Merry Christmas!",
|
||||
2: "Happy New Year!",
|
||||
},
|
||||
},
|
||||
{
|
||||
# peace soundtrack hatsubai kinen SP event, 0 = off, 1 = active, 2 = off (0-2)
|
||||
'name': 'peace soundtrack hatsubai kinen SP',
|
||||
'tip': 'peace soundtrack hatsubai kinen SP for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'peace_soundtrack',
|
||||
'values': {
|
||||
0: 'Not stated',
|
||||
1: 'Active',
|
||||
2: 'Ended',
|
||||
}
|
||||
"name": "peace soundtrack hatsubai kinen SP",
|
||||
"tip": "peace soundtrack hatsubai kinen SP for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "peace_soundtrack",
|
||||
"values": {
|
||||
0: "Not stated",
|
||||
1: "Active",
|
||||
2: "Ended",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'MZD no kimagure tanteisha joshu',
|
||||
'tip': 'Boost increasing the Clarification Level, if four or more Requests still unresolved.',
|
||||
'category': 'game_config',
|
||||
'setting': 'tanteisha_joshu',
|
||||
'values': {
|
||||
0: 'Not stated',
|
||||
1: 'Active',
|
||||
2: 'Ended',
|
||||
}
|
||||
"name": "MZD no kimagure tanteisha joshu",
|
||||
"tip": "Boost increasing the Clarification Level, if four or more Requests still unresolved.",
|
||||
"category": "game_config",
|
||||
"setting": "tanteisha_joshu",
|
||||
"values": {
|
||||
0: "Not stated",
|
||||
1: "Active",
|
||||
2: "Ended",
|
||||
},
|
||||
},
|
||||
{
|
||||
# Shutchou! pop'n quest Lively II event
|
||||
'name': 'Shutchou! pop\'n quest Lively phase',
|
||||
'tip': 'Shutchou! pop\'n quest Lively phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'popn_quest_lively',
|
||||
'values': {
|
||||
0: 'Not started',
|
||||
1: 'fes 1',
|
||||
2: 'fes 2',
|
||||
3: 'fes FINAL',
|
||||
4: 'fes EXTRA',
|
||||
5: 'Ended',
|
||||
}
|
||||
"name": "Shutchou! pop'n quest Lively phase",
|
||||
"tip": "Shutchou! pop'n quest Lively phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "popn_quest_lively",
|
||||
"values": {
|
||||
0: "Not started",
|
||||
1: "fes 1",
|
||||
2: "fes 2",
|
||||
3: "fes FINAL",
|
||||
4: "fes EXTRA",
|
||||
5: "Ended",
|
||||
},
|
||||
},
|
||||
{
|
||||
# Shutchou! pop'n quest Lively II event
|
||||
'name': 'Shutchou! pop\'n quest Lively II phase',
|
||||
'tip': 'Shutchou! pop\'n quest Lively II phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'popn_quest_lively_2',
|
||||
'values': {
|
||||
0: 'Not started',
|
||||
1: 'fes 1',
|
||||
2: 'fes 2',
|
||||
3: 'fes FINAL',
|
||||
4: 'fes EXTRA',
|
||||
5: 'fes THE END',
|
||||
6: 'Ended',
|
||||
}
|
||||
"name": "Shutchou! pop'n quest Lively II phase",
|
||||
"tip": "Shutchou! pop'n quest Lively II phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "popn_quest_lively_2",
|
||||
"values": {
|
||||
0: "Not started",
|
||||
1: "fes 1",
|
||||
2: "fes 2",
|
||||
3: "fes FINAL",
|
||||
4: "fes EXTRA",
|
||||
5: "fes THE END",
|
||||
6: "Ended",
|
||||
},
|
||||
},
|
||||
],
|
||||
'bools': [
|
||||
"bools": [
|
||||
# We don't currently support lobbies or anything, so this is commented out until
|
||||
# somebody gets around to implementing it.
|
||||
# {
|
||||
@ -208,24 +208,24 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
||||
# 'setting': 'enable_net_taisen',
|
||||
# },
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||
game_config = self.get_game_config()
|
||||
music_phase = game_config.get_int('music_phase')
|
||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
||||
music_phase = game_config.get_int("music_phase")
|
||||
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||||
mn_tanteisha_phase = game_config.get_int('mn_tanteisha_phase')
|
||||
peace_soundtrack = game_config.get_int('peace_soundtrack')
|
||||
tanteisha_joshu = game_config.get_int('tanteisha_joshu')
|
||||
popn_quest_lively = game_config.get_int('popn_quest_lively')
|
||||
popn_quest_lively_2 = game_config.get_int('popn_quest_lively_2')
|
||||
mn_tanteisha_phase = game_config.get_int("mn_tanteisha_phase")
|
||||
peace_soundtrack = game_config.get_int("peace_soundtrack")
|
||||
tanteisha_joshu = game_config.get_int("tanteisha_joshu")
|
||||
popn_quest_lively = game_config.get_int("popn_quest_lively")
|
||||
popn_quest_lively_2 = game_config.get_int("popn_quest_lively_2")
|
||||
|
||||
# Event phases
|
||||
return (
|
||||
@ -384,20 +384,31 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = super().format_profile(userid, profile)
|
||||
|
||||
account = root.child('account')
|
||||
account.add_child(Node.s16('card_again_count', profile.get_int('card_again_count')))
|
||||
account.add_child(Node.s16('sp_riddles_id', profile.get_int('sp_riddles_id')))
|
||||
account = root.child("account")
|
||||
account.add_child(
|
||||
Node.s16("card_again_count", profile.get_int("card_again_count"))
|
||||
)
|
||||
account.add_child(Node.s16("sp_riddles_id", profile.get_int("sp_riddles_id")))
|
||||
|
||||
# Kaimei riddles events
|
||||
event2021 = Node.void('event2021')
|
||||
event2021 = Node.void("event2021")
|
||||
root.add_child(event2021)
|
||||
event2021.add_child(Node.u32('point', profile.get_int('point')))
|
||||
event2021.add_child(Node.u8('step', profile.get_int('step')))
|
||||
event2021.add_child(Node.u32_array('quest_point', profile.get_int_array('quest_point', 8, [0] * 8)))
|
||||
event2021.add_child(Node.u8('step_nos', profile.get_int('step_nos')))
|
||||
event2021.add_child(Node.u32_array('quest_point_nos', profile.get_int_array('quest_point_nos', 13, [0] * 13)))
|
||||
event2021.add_child(Node.u32("point", profile.get_int("point")))
|
||||
event2021.add_child(Node.u8("step", profile.get_int("step")))
|
||||
event2021.add_child(
|
||||
Node.u32_array(
|
||||
"quest_point", profile.get_int_array("quest_point", 8, [0] * 8)
|
||||
)
|
||||
)
|
||||
event2021.add_child(Node.u8("step_nos", profile.get_int("step_nos")))
|
||||
event2021.add_child(
|
||||
Node.u32_array(
|
||||
"quest_point_nos",
|
||||
profile.get_int_array("quest_point_nos", 13, [0] * 13),
|
||||
)
|
||||
)
|
||||
|
||||
riddles_data = Node.void('riddles_data')
|
||||
riddles_data = Node.void("riddles_data")
|
||||
root.add_child(riddles_data)
|
||||
|
||||
# Generate Short Riddles for MN tanteisha
|
||||
@ -413,62 +424,74 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
||||
|
||||
randomRiddles.append(riddle)
|
||||
|
||||
sh_riddles = Node.void('sh_riddles')
|
||||
sh_riddles = Node.void("sh_riddles")
|
||||
riddles_data.add_child(sh_riddles)
|
||||
sh_riddles.add_child(Node.u32('sh_riddles_id', riddle))
|
||||
sh_riddles.add_child(Node.u32("sh_riddles_id", riddle))
|
||||
|
||||
# Set up kaimei riddles achievements
|
||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||
achievements = self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
for achievement in achievements:
|
||||
if achievement.type == 'riddle':
|
||||
kaimei_gauge = achievement.data.get_int('kaimei_gauge')
|
||||
is_cleared = achievement.data.get_bool('is_cleared')
|
||||
riddles_cleared = achievement.data.get_bool('riddles_cleared')
|
||||
select_count = achievement.data.get_int('select_count')
|
||||
other_count = achievement.data.get_int('other_count')
|
||||
if achievement.type == "riddle":
|
||||
kaimei_gauge = achievement.data.get_int("kaimei_gauge")
|
||||
is_cleared = achievement.data.get_bool("is_cleared")
|
||||
riddles_cleared = achievement.data.get_bool("riddles_cleared")
|
||||
select_count = achievement.data.get_int("select_count")
|
||||
other_count = achievement.data.get_int("other_count")
|
||||
|
||||
sp_riddles = Node.void('sp_riddles')
|
||||
sp_riddles = Node.void("sp_riddles")
|
||||
riddles_data.add_child(sp_riddles)
|
||||
sp_riddles.add_child(Node.u16('kaimei_gauge', kaimei_gauge))
|
||||
sp_riddles.add_child(Node.bool('is_cleared', is_cleared))
|
||||
sp_riddles.add_child(Node.bool('riddles_cleared', riddles_cleared))
|
||||
sp_riddles.add_child(Node.u8('select_count', select_count))
|
||||
sp_riddles.add_child(Node.u32('other_count', other_count))
|
||||
sp_riddles.add_child(Node.u16("kaimei_gauge", kaimei_gauge))
|
||||
sp_riddles.add_child(Node.bool("is_cleared", is_cleared))
|
||||
sp_riddles.add_child(Node.bool("riddles_cleared", riddles_cleared))
|
||||
sp_riddles.add_child(Node.u8("select_count", select_count))
|
||||
sp_riddles.add_child(Node.u32("other_count", other_count))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = super().unformat_profile(userid, request, oldprofile)
|
||||
|
||||
account = request.child('account')
|
||||
account = request.child("account")
|
||||
if account is not None:
|
||||
newprofile.replace_int('card_again_count', account.child_value('card_again_count'))
|
||||
newprofile.replace_int('sp_riddles_id', account.child_value('sp_riddles_id'))
|
||||
newprofile.replace_int(
|
||||
"card_again_count", account.child_value("card_again_count")
|
||||
)
|
||||
newprofile.replace_int(
|
||||
"sp_riddles_id", account.child_value("sp_riddles_id")
|
||||
)
|
||||
|
||||
# Kaimei riddles events
|
||||
event2021 = request.child('event2021')
|
||||
event2021 = request.child("event2021")
|
||||
if event2021 is not None:
|
||||
newprofile.replace_int('point', event2021.child_value('point'))
|
||||
newprofile.replace_int('step', event2021.child_value('step'))
|
||||
newprofile.replace_int_array('quest_point', 8, event2021.child_value('quest_point'))
|
||||
newprofile.replace_int('step_nos', event2021.child_value('step_nos'))
|
||||
newprofile.replace_int_array('quest_point_nos', 13, event2021.child_value('quest_point_nos'))
|
||||
newprofile.replace_int("point", event2021.child_value("point"))
|
||||
newprofile.replace_int("step", event2021.child_value("step"))
|
||||
newprofile.replace_int_array(
|
||||
"quest_point", 8, event2021.child_value("quest_point")
|
||||
)
|
||||
newprofile.replace_int("step_nos", event2021.child_value("step_nos"))
|
||||
newprofile.replace_int_array(
|
||||
"quest_point_nos", 13, event2021.child_value("quest_point_nos")
|
||||
)
|
||||
|
||||
# Extract kaimei riddles achievements
|
||||
for node in request.children:
|
||||
if node.name == 'riddles_data':
|
||||
if node.name == "riddles_data":
|
||||
riddle_id = 0
|
||||
playedRiddle = request.child('account').child_value('sp_riddles_id')
|
||||
playedRiddle = request.child("account").child_value("sp_riddles_id")
|
||||
for riddle in node.children:
|
||||
kaimei_gauge = riddle.child_value('kaimei_gauge')
|
||||
is_cleared = riddle.child_value('is_cleared')
|
||||
riddles_cleared = riddle.child_value('riddles_cleared')
|
||||
select_count = riddle.child_value('select_count')
|
||||
other_count = riddle.child_value('other_count')
|
||||
kaimei_gauge = riddle.child_value("kaimei_gauge")
|
||||
is_cleared = riddle.child_value("is_cleared")
|
||||
riddles_cleared = riddle.child_value("riddles_cleared")
|
||||
select_count = riddle.child_value("select_count")
|
||||
other_count = riddle.child_value("other_count")
|
||||
|
||||
if (riddles_cleared or select_count >= 3):
|
||||
if riddles_cleared or select_count >= 3:
|
||||
select_count = 3
|
||||
elif (playedRiddle == riddle_id):
|
||||
elif playedRiddle == riddle_id:
|
||||
select_count += 1
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
@ -476,13 +499,13 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
||||
self.version,
|
||||
userid,
|
||||
riddle_id,
|
||||
'riddle',
|
||||
"riddle",
|
||||
{
|
||||
'kaimei_gauge': kaimei_gauge,
|
||||
'is_cleared': is_cleared,
|
||||
'riddles_cleared': riddles_cleared,
|
||||
'select_count': select_count,
|
||||
'other_count': other_count,
|
||||
"kaimei_gauge": kaimei_gauge,
|
||||
"is_cleared": is_cleared,
|
||||
"riddles_cleared": riddles_cleared,
|
||||
"select_count": select_count,
|
||||
"other_count": other_count,
|
||||
},
|
||||
)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,102 +36,102 @@ class PopnMusicPeace(PopnMusicModernBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'ints': [
|
||||
"ints": [
|
||||
{
|
||||
'name': 'Music Open Phase',
|
||||
'tip': 'Default music phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'music_phase',
|
||||
'values': {
|
||||
"name": "Music Open Phase",
|
||||
"tip": "Default music phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "music_phase",
|
||||
"values": {
|
||||
# The value goes to 23 now, but it starts where usaneko left off at 11
|
||||
# Unlocks a total of 53 songs
|
||||
12: 'No music unlocks',
|
||||
13: 'Phase 1',
|
||||
14: 'Phase 2',
|
||||
15: 'Phase 3',
|
||||
16: 'Phase 4',
|
||||
17: 'Phase 5',
|
||||
18: 'Phase 6',
|
||||
19: 'Phase 7',
|
||||
20: 'Phase 8',
|
||||
21: 'Phase 9',
|
||||
22: 'Phase 10',
|
||||
23: 'Phase MAX',
|
||||
}
|
||||
12: "No music unlocks",
|
||||
13: "Phase 1",
|
||||
14: "Phase 2",
|
||||
15: "Phase 3",
|
||||
16: "Phase 4",
|
||||
17: "Phase 5",
|
||||
18: "Phase 6",
|
||||
19: "Phase 7",
|
||||
20: "Phase 8",
|
||||
21: "Phase 9",
|
||||
22: "Phase 10",
|
||||
23: "Phase MAX",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'NAVI-Kun Event Phase',
|
||||
'tip': 'NAVI-Kun event phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'navikun_phase',
|
||||
'values': {
|
||||
"name": "NAVI-Kun Event Phase",
|
||||
"tip": "NAVI-Kun event phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "navikun_phase",
|
||||
"values": {
|
||||
# The value goes to 30 now, but it starts where usaneko left off at 15
|
||||
# Unlocks a total of 89 songs
|
||||
15: 'Phase 1',
|
||||
16: 'Phase 2',
|
||||
17: 'Phase 3',
|
||||
18: 'Phase 4',
|
||||
19: 'Phase 5',
|
||||
20: 'Phase 6',
|
||||
21: 'Phase 7',
|
||||
22: 'Phase 8',
|
||||
23: 'Phase 9',
|
||||
24: 'Phase 10',
|
||||
25: 'Phase 11',
|
||||
26: 'Phase 12',
|
||||
27: 'Phase 13',
|
||||
28: 'Phase 14',
|
||||
29: 'Phase 15',
|
||||
30: 'Phase MAX',
|
||||
15: "Phase 1",
|
||||
16: "Phase 2",
|
||||
17: "Phase 3",
|
||||
18: "Phase 4",
|
||||
19: "Phase 5",
|
||||
20: "Phase 6",
|
||||
21: "Phase 7",
|
||||
22: "Phase 8",
|
||||
23: "Phase 9",
|
||||
24: "Phase 10",
|
||||
25: "Phase 11",
|
||||
26: "Phase 12",
|
||||
27: "Phase 13",
|
||||
28: "Phase 14",
|
||||
29: "Phase 15",
|
||||
30: "Phase MAX",
|
||||
},
|
||||
},
|
||||
{
|
||||
# For festive times, it's possible to change the welcome greeting. I'm not sure why you would want to change this, but now you can.
|
||||
'name': 'Holiday Greeting',
|
||||
'tip': 'Changes the payment selection confirmation sound.',
|
||||
'category': 'game_config',
|
||||
'setting': 'holiday_greeting',
|
||||
'values': {
|
||||
0: 'Okay!',
|
||||
1: 'Merry Christmas!',
|
||||
2: 'Happy New Year!',
|
||||
}
|
||||
"name": "Holiday Greeting",
|
||||
"tip": "Changes the payment selection confirmation sound.",
|
||||
"category": "game_config",
|
||||
"setting": "holiday_greeting",
|
||||
"values": {
|
||||
0: "Okay!",
|
||||
1: "Merry Christmas!",
|
||||
2: "Happy New Year!",
|
||||
},
|
||||
},
|
||||
{
|
||||
# The following values control the pop'n music event archive. Setting the flag to the following values has the
|
||||
# corresponding effect. Each value will include the events above it, for example setting it to 5 gives you the
|
||||
# pop'n 15 event, as well as SP, 12, and 11 events. Setting it to 0 disabled the event and skips the entire screen,
|
||||
# setting it to 20 makes all of the events available for selection. Completing the minigame unlocks the associated content.
|
||||
'name': 'Event Archive Phase',
|
||||
'tip': 'Event Archive mini-game phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'event_archive_phase',
|
||||
'values': {
|
||||
0: 'Event Archive disabled',
|
||||
1: 'pop\'n music 11 - The Latest Space Station',
|
||||
2: 'pop\'n music 11 & 12 Iroha - The Southernmost Point of the Universe / Ninja Otasuke Cheat Sheet in Trouble',
|
||||
3: 'pop\'n music Sunny Park - I Love Walking in Happiness Park',
|
||||
4: 'pop\'n music 12 Iroha - Ninja Code: April 1st Volume',
|
||||
5: 'pop\'n music 15 ADVENTURE - Route to Awaken the Soul',
|
||||
6: 'pop\'n music 20 fantasia - A Braided Fantasy Song',
|
||||
7: 'EXTRA',
|
||||
8: 'pop\'n music 15 ADVENTURE - A Route with a Faint Bell Sound',
|
||||
9: 'pop\'n music 13 Carnival - Bunny Magician Attraction',
|
||||
10: 'pop\'n music 14 FEVER! - That Burning Special Attack, again!',
|
||||
11: 'pop\'n music Sunny Park - Festival Nightfall Park',
|
||||
12: 'pop\'n music 20 fantasia - A Fantasy Song by the Bladed Warrior',
|
||||
13: 'pop\'n music 19 TUNE STREET - A Town Where the Sound of the Brass Band Rings After School',
|
||||
14: 'pop\'n music éclale - Fun Rag Hour',
|
||||
15: 'pop\'n music 13 Carnival - Ghost Piano Attraction',
|
||||
16: 'pop\'n music 14 FEVER! - That Warrior Defending Peace, again!',
|
||||
17: 'pop\'n music 18 Sengoku Retsuden - A Territory with a Glamorous Cultural Flavor',
|
||||
18: 'pop\'n music éclale - Runaway Guitarist in the Starry Sky',
|
||||
19: 'pop\'n music 17 THE MOVIE - A Blockbuster Uncovering a Conspiracy in the Peaceful City',
|
||||
20: 'pop\'n music lapistoria - God\'s Forgotten Things',
|
||||
}
|
||||
"name": "Event Archive Phase",
|
||||
"tip": "Event Archive mini-game phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "event_archive_phase",
|
||||
"values": {
|
||||
0: "Event Archive disabled",
|
||||
1: "pop'n music 11 - The Latest Space Station",
|
||||
2: "pop'n music 11 & 12 Iroha - The Southernmost Point of the Universe / Ninja Otasuke Cheat Sheet in Trouble",
|
||||
3: "pop'n music Sunny Park - I Love Walking in Happiness Park",
|
||||
4: "pop'n music 12 Iroha - Ninja Code: April 1st Volume",
|
||||
5: "pop'n music 15 ADVENTURE - Route to Awaken the Soul",
|
||||
6: "pop'n music 20 fantasia - A Braided Fantasy Song",
|
||||
7: "EXTRA",
|
||||
8: "pop'n music 15 ADVENTURE - A Route with a Faint Bell Sound",
|
||||
9: "pop'n music 13 Carnival - Bunny Magician Attraction",
|
||||
10: "pop'n music 14 FEVER! - That Burning Special Attack, again!",
|
||||
11: "pop'n music Sunny Park - Festival Nightfall Park",
|
||||
12: "pop'n music 20 fantasia - A Fantasy Song by the Bladed Warrior",
|
||||
13: "pop'n music 19 TUNE STREET - A Town Where the Sound of the Brass Band Rings After School",
|
||||
14: "pop'n music éclale - Fun Rag Hour",
|
||||
15: "pop'n music 13 Carnival - Ghost Piano Attraction",
|
||||
16: "pop'n music 14 FEVER! - That Warrior Defending Peace, again!",
|
||||
17: "pop'n music 18 Sengoku Retsuden - A Territory with a Glamorous Cultural Flavor",
|
||||
18: "pop'n music éclale - Runaway Guitarist in the Starry Sky",
|
||||
19: "pop'n music 17 THE MOVIE - A Blockbuster Uncovering a Conspiracy in the Peaceful City",
|
||||
20: "pop'n music lapistoria - God's Forgotten Things",
|
||||
},
|
||||
},
|
||||
],
|
||||
'bools': [
|
||||
"bools": [
|
||||
# We don't currently support lobbies or anything, so this is commented out until
|
||||
# somebody gets around to implementing it.
|
||||
# {
|
||||
@ -141,21 +141,21 @@ class PopnMusicPeace(PopnMusicModernBase):
|
||||
# 'setting': 'enable_net_taisen',
|
||||
# },
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||
game_config = self.get_game_config()
|
||||
music_phase = game_config.get_int('music_phase')
|
||||
event_archive_phase = game_config.get_int('event_archive_phase')
|
||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
||||
music_phase = game_config.get_int("music_phase")
|
||||
event_archive_phase = game_config.get_int("event_archive_phase")
|
||||
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||||
navikun_phase = game_config.get_int('navikun_phase')
|
||||
navikun_phase = game_config.get_int("navikun_phase")
|
||||
|
||||
# Event phases
|
||||
return (
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -66,64 +66,64 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'ints': [
|
||||
"ints": [
|
||||
{
|
||||
'name': 'Game Phase',
|
||||
'tip': 'Game unlock phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'game_phase',
|
||||
'values': {
|
||||
0: 'NO PHASE',
|
||||
1: 'SECRET DATA RELEASE',
|
||||
2: 'MAX: ALL DATA RELEASE',
|
||||
}
|
||||
"name": "Game Phase",
|
||||
"tip": "Game unlock phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "game_phase",
|
||||
"values": {
|
||||
0: "NO PHASE",
|
||||
1: "SECRET DATA RELEASE",
|
||||
2: "MAX: ALL DATA RELEASE",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'Town Mode Phase',
|
||||
'tip': 'Town mode phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'town_phase',
|
||||
'values': {
|
||||
0: 'town mode disabled',
|
||||
1: 'town phase 1',
|
||||
2: 'town phase 2',
|
||||
3: 'Pop\'n Naan Festival',
|
||||
"name": "Town Mode Phase",
|
||||
"tip": "Town mode phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "town_phase",
|
||||
"values": {
|
||||
0: "town mode disabled",
|
||||
1: "town phase 1",
|
||||
2: "town phase 2",
|
||||
3: "Pop'n Naan Festival",
|
||||
# 4 seems to be a continuation of town phase 2. Intentionally leaving it out.
|
||||
5: 'town phase 3',
|
||||
6: 'town phase 4',
|
||||
7: 'Miracle 4 + 1',
|
||||
5: "town phase 3",
|
||||
6: "town phase 4",
|
||||
7: "Miracle 4 + 1",
|
||||
# 8 seems to be a continuation of town phase 4. Intentionally leaving it out.
|
||||
9: 'town phase MAX',
|
||||
10: 'Find your daughter!',
|
||||
9: "town phase MAX",
|
||||
10: "Find your daughter!",
|
||||
# 11 is a continuation of phase MAX after find your daughter, with Tanabata
|
||||
# bamboo grass added as well.
|
||||
11: 'town phase MAX+1',
|
||||
12: 'Peruri-san visits',
|
||||
11: "town phase MAX+1",
|
||||
12: "Peruri-san visits",
|
||||
# 13 is a continuation of phase MAX+1 after peruri-san visits, with Watermelon
|
||||
# pattern tank added as well.
|
||||
13: 'town phase MAX+2',
|
||||
14: 'Find Deuil!',
|
||||
13: "town phase MAX+2",
|
||||
14: "Find Deuil!",
|
||||
# 15 is a continuation of phase MAX+2 after find deuil, with Tsukimi dumplings
|
||||
# added as well.
|
||||
15: 'town phase MAX+3',
|
||||
16: 'Landmark stamp rally',
|
||||
15: "town phase MAX+3",
|
||||
16: "Landmark stamp rally",
|
||||
# 17 is a continuation of MAX+3 after landmark stamp rally ends, but offering
|
||||
# no additional stuff.
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
{
|
||||
'name': 'Force Customization Unlock',
|
||||
'tip': 'Force unlock all theme and menu customizations.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_customizations',
|
||||
"name": "Force Customization Unlock",
|
||||
"tip": "Force unlock all theme and menu customizations.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_customizations",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -166,11 +166,11 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: self.GAME_PLAY_FLAG_FULL_COMBO,
|
||||
self.PLAY_MEDAL_STAR_FULL_COMBO: self.GAME_PLAY_FLAG_FULL_COMBO,
|
||||
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_FLAG_PERFECT_COMBO,
|
||||
}[score.data.get_int('medal')]
|
||||
}[score.data.get_int("medal")]
|
||||
return (flags << shift) | playedflag
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
root = Node.void('playerdata')
|
||||
root = Node.void("playerdata")
|
||||
|
||||
# Format profile
|
||||
binary_profile = [0] * 2198
|
||||
@ -178,7 +178,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
# Copy name. We intentionally leave location 12 alone as it is
|
||||
# the null termination for the name if it happens to be 12
|
||||
# characters (6 shift-jis kana).
|
||||
name_binary = profile.get_str('name', 'なし').encode('shift-jis')[0:12]
|
||||
name_binary = profile.get_str("name", "なし").encode("shift-jis")[0:12]
|
||||
for name_pos, byte in enumerate(name_binary):
|
||||
binary_profile[name_pos] = byte
|
||||
|
||||
@ -199,32 +199,32 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
13: 5,
|
||||
14: 5,
|
||||
15: 5,
|
||||
}[profile.get_int('play_mode')]
|
||||
}[profile.get_int("play_mode")]
|
||||
|
||||
# Copy miscelaneous values
|
||||
binary_profile[15] = profile.get_int('last_play_flag') & 0xFF
|
||||
binary_profile[16] = profile.get_int('medal_and_friend') & 0xFF
|
||||
binary_profile[37] = profile.get_int('read_news') & 0xFF
|
||||
binary_profile[38] = profile.get_int('skin_tex_note') & 0xFF
|
||||
binary_profile[39] = profile.get_int('skin_tex_cmn') & 0xFF
|
||||
binary_profile[40] = profile.get_int('skin_sd_bgm') & 0xFF
|
||||
binary_profile[41] = profile.get_int('skin_sd_se') & 0xFF
|
||||
binary_profile[44] = profile.get_int('option') & 0xFF
|
||||
binary_profile[45] = (profile.get_int('option') >> 8) & 0xFF
|
||||
binary_profile[46] = (profile.get_int('option') >> 16) & 0xFF
|
||||
binary_profile[47] = (profile.get_int('option') >> 24) & 0xFF
|
||||
binary_profile[48] = profile.get_int('jubeat_collabo') & 0xFF
|
||||
binary_profile[49] = (profile.get_int('jubeat_collabo') >> 8) & 0xFF
|
||||
binary_profile[15] = profile.get_int("last_play_flag") & 0xFF
|
||||
binary_profile[16] = profile.get_int("medal_and_friend") & 0xFF
|
||||
binary_profile[37] = profile.get_int("read_news") & 0xFF
|
||||
binary_profile[38] = profile.get_int("skin_tex_note") & 0xFF
|
||||
binary_profile[39] = profile.get_int("skin_tex_cmn") & 0xFF
|
||||
binary_profile[40] = profile.get_int("skin_sd_bgm") & 0xFF
|
||||
binary_profile[41] = profile.get_int("skin_sd_se") & 0xFF
|
||||
binary_profile[44] = profile.get_int("option") & 0xFF
|
||||
binary_profile[45] = (profile.get_int("option") >> 8) & 0xFF
|
||||
binary_profile[46] = (profile.get_int("option") >> 16) & 0xFF
|
||||
binary_profile[47] = (profile.get_int("option") >> 24) & 0xFF
|
||||
binary_profile[48] = profile.get_int("jubeat_collabo") & 0xFF
|
||||
binary_profile[49] = (profile.get_int("jubeat_collabo") >> 8) & 0xFF
|
||||
|
||||
# 52-56 and 56-60 make up two 32 bit colors found in color_3p_flag.
|
||||
binary_profile[60] = profile.get_int('chara', -1) & 0xFF
|
||||
binary_profile[61] = (profile.get_int('chara', -1) >> 8) & 0xFF
|
||||
binary_profile[62] = profile.get_int('music') & 0xFF
|
||||
binary_profile[63] = (profile.get_int('music') >> 8) & 0xFF
|
||||
binary_profile[64] = profile.get_int('sheet') & 0xFF
|
||||
binary_profile[65] = profile.get_int('category') & 0xFF
|
||||
binary_profile[66] = profile.get_int('norma_point') & 0xFF
|
||||
binary_profile[67] = (profile.get_int('norma_point') >> 8) & 0xFF
|
||||
binary_profile[60] = profile.get_int("chara", -1) & 0xFF
|
||||
binary_profile[61] = (profile.get_int("chara", -1) >> 8) & 0xFF
|
||||
binary_profile[62] = profile.get_int("music") & 0xFF
|
||||
binary_profile[63] = (profile.get_int("music") >> 8) & 0xFF
|
||||
binary_profile[64] = profile.get_int("sheet") & 0xFF
|
||||
binary_profile[65] = profile.get_int("category") & 0xFF
|
||||
binary_profile[66] = profile.get_int("norma_point") & 0xFF
|
||||
binary_profile[67] = (profile.get_int("norma_point") >> 8) & 0xFF
|
||||
|
||||
# Format Scores
|
||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 7) * 17) + 7) / 8)
|
||||
@ -238,14 +238,18 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
self.CHART_TYPE_EASY,
|
||||
]:
|
||||
continue
|
||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
||||
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||
continue
|
||||
|
||||
flags = self.__format_flags_for_score(score)
|
||||
|
||||
flags_index = score.id * 2
|
||||
binary_profile[108 + flags_index] = binary_profile[108 + flags_index] | (flags & 0xFF)
|
||||
binary_profile[109 + flags_index] = binary_profile[109 + flags_index] | ((flags >> 8) & 0xFF)
|
||||
binary_profile[108 + flags_index] = binary_profile[108 + flags_index] | (
|
||||
flags & 0xFF
|
||||
)
|
||||
binary_profile[109 + flags_index] = binary_profile[109 + flags_index] | (
|
||||
(flags >> 8) & 0xFF
|
||||
)
|
||||
|
||||
if score.chart in [
|
||||
self.CHART_TYPE_ENJOY_5_BUTTON,
|
||||
@ -267,12 +271,23 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||
hiscore_value = score.points << hiscore_bit_pos
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (hiscore_value & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos] = hiscore_array[hiscore_byte_pos] | (
|
||||
hiscore_value & 0xFF
|
||||
)
|
||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[
|
||||
hiscore_byte_pos + 1
|
||||
] | ((hiscore_value >> 8) & 0xFF)
|
||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[
|
||||
hiscore_byte_pos + 2
|
||||
] | ((hiscore_value >> 16) & 0xFF)
|
||||
|
||||
# Format most played
|
||||
most_played = [x[0] for x in self.data.local.music.get_most_played(self.game, self.version, userid, 20)]
|
||||
most_played = [
|
||||
x[0]
|
||||
for x in self.data.local.music.get_most_played(
|
||||
self.game, self.version, userid, 20
|
||||
)
|
||||
]
|
||||
while len(most_played) < 20:
|
||||
most_played.append(-1)
|
||||
profile_pos = 68
|
||||
@ -291,22 +306,24 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
# - 10 appears to be purchased BGMs.
|
||||
# - 11 appears to be purchased sound effects.
|
||||
binary_town = [0] * 141
|
||||
town = profile.get_dict('town')
|
||||
town = profile.get_dict("town")
|
||||
|
||||
# Last play flag, so the selection for 5/9/9+cool sticks.
|
||||
binary_town[140] = town.get_int('play_type')
|
||||
binary_town[140] = town.get_int("play_type")
|
||||
|
||||
# Fill in basic town points, tracked here and returned in basic profile for some reason.
|
||||
binary_town[0] = town.get_int('points') & 0xFF
|
||||
binary_town[1] = (town.get_int('points') >> 8) & 0xFF
|
||||
binary_town[2] = (town.get_int('points') >> 16) & 0xFF
|
||||
binary_town[3] = (town.get_int('points') >> 24) & 0xFF
|
||||
binary_town[0] = town.get_int("points") & 0xFF
|
||||
binary_town[1] = (town.get_int("points") >> 8) & 0xFF
|
||||
binary_town[2] = (town.get_int("points") >> 16) & 0xFF
|
||||
binary_town[3] = (town.get_int("points") >> 24) & 0xFF
|
||||
|
||||
# Fill in purchase flags (this is for stuff like BGMs, SEs, Pop-kun customizations, etc).
|
||||
bought_flg = town.get_int_array('bought_flg', 3)
|
||||
bought_flg = town.get_int_array("bought_flg", 3)
|
||||
game_config = self.get_game_config()
|
||||
force_unlock_songs = game_config.get_bool('force_unlock_songs')
|
||||
force_unlock_customizations = game_config.get_bool('force_unlock_customizations')
|
||||
force_unlock_songs = game_config.get_bool("force_unlock_songs")
|
||||
force_unlock_customizations = game_config.get_bool(
|
||||
"force_unlock_customizations"
|
||||
)
|
||||
|
||||
if force_unlock_songs:
|
||||
bought_flg[0] = 0xFFFFFFFF
|
||||
@ -320,7 +337,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
binary_town[off + 3] = (bought_flg[flg] >> 24) & 0xFF
|
||||
|
||||
# Fill in build flags (presumably for what parcels of land have been bought and built on).
|
||||
build_flg = town.get_int_array('build_flg', 8)
|
||||
build_flg = town.get_int_array("build_flg", 8)
|
||||
for flg, off in enumerate([16, 20, 24, 28, 32, 36, 40, 44]):
|
||||
binary_town[off + 0] = build_flg[flg] & 0xFF
|
||||
binary_town[off + 1] = (build_flg[flg] >> 8) & 0xFF
|
||||
@ -328,15 +345,37 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
binary_town[off + 3] = (build_flg[flg] >> 24) & 0xFF
|
||||
|
||||
# Fill in character flags (presumably for character location, orientation, stats, etc).
|
||||
chara_flg = town.get_int_array('chara_flg', 19)
|
||||
for flg, off in enumerate([48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120]):
|
||||
chara_flg = town.get_int_array("chara_flg", 19)
|
||||
for flg, off in enumerate(
|
||||
[
|
||||
48,
|
||||
52,
|
||||
56,
|
||||
60,
|
||||
64,
|
||||
68,
|
||||
72,
|
||||
76,
|
||||
80,
|
||||
84,
|
||||
88,
|
||||
92,
|
||||
96,
|
||||
100,
|
||||
104,
|
||||
108,
|
||||
112,
|
||||
116,
|
||||
120,
|
||||
]
|
||||
):
|
||||
binary_town[off + 0] = chara_flg[flg] & 0xFF
|
||||
binary_town[off + 1] = (chara_flg[flg] >> 8) & 0xFF
|
||||
binary_town[off + 2] = (chara_flg[flg] >> 16) & 0xFF
|
||||
binary_town[off + 3] = (chara_flg[flg] >> 24) & 0xFF
|
||||
|
||||
# Fill in miscellaneous event flags.
|
||||
event_flg = town.get_int_array('event_flg', 4)
|
||||
event_flg = town.get_int_array("event_flg", 4)
|
||||
for flg, off in enumerate([124, 128, 132, 136]):
|
||||
binary_town[off + 0] = event_flg[flg] & 0xFF
|
||||
binary_town[off + 1] = (event_flg[flg] >> 8) & 0xFF
|
||||
@ -344,58 +383,72 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
binary_town[off + 3] = (event_flg[flg] >> 24) & 0xFF
|
||||
|
||||
# Construct final profile
|
||||
root.add_child(Node.binary('b', bytes(binary_profile)))
|
||||
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
|
||||
root.add_child(Node.binary('town', bytes(binary_town)))
|
||||
root.add_child(Node.binary("b", bytes(binary_profile)))
|
||||
root.add_child(Node.binary("hiscore", bytes(hiscore_array)))
|
||||
root.add_child(Node.binary("town", bytes(binary_town)))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
|
||||
# Extract the playmode, important for scores later
|
||||
playmode = int(request.attribute('play_mode'))
|
||||
newprofile.replace_int('play_mode', playmode)
|
||||
playmode = int(request.attribute("play_mode"))
|
||||
newprofile.replace_int("play_mode", playmode)
|
||||
|
||||
# Extract profile options
|
||||
newprofile.replace_int('chara', int(request.attribute('chara_num')))
|
||||
if 'option' in request.attributes:
|
||||
newprofile.replace_int('option', int(request.attribute('option')))
|
||||
if 'last_play_flag' in request.attributes:
|
||||
newprofile.replace_int('last_play_flag', int(request.attribute('last_play_flag')))
|
||||
if 'medal_and_friend' in request.attributes:
|
||||
newprofile.replace_int('medal_and_friend', int(request.attribute('medal_and_friend')))
|
||||
if 'music_num' in request.attributes:
|
||||
newprofile.replace_int('music', int(request.attribute('music_num')))
|
||||
if 'sheet_num' in request.attributes:
|
||||
newprofile.replace_int('sheet', int(request.attribute('sheet_num')))
|
||||
if 'category_num' in request.attributes:
|
||||
newprofile.replace_int('category', int(request.attribute('category_num')))
|
||||
if 'read_news_no_max' in request.attributes:
|
||||
newprofile.replace_int('read_news', int(request.attribute('read_news_no_max')))
|
||||
if 'jubeat_collabo' in request.attributes:
|
||||
newprofile.replace_int('jubeat_collabo', int(request.attribute('jubeat_collabo')))
|
||||
if 'norma_point' in request.attributes:
|
||||
newprofile.replace_int('norma_point', int(request.attribute('norma_point')))
|
||||
if 'skin_tex_note' in request.attributes:
|
||||
newprofile.replace_int('skin_tex_note', int(request.attribute('skin_tex_note')))
|
||||
if 'skin_tex_cmn' in request.attributes:
|
||||
newprofile.replace_int('skin_tex_cmn', int(request.attribute('skin_tex_cmn')))
|
||||
if 'skin_sd_bgm' in request.attributes:
|
||||
newprofile.replace_int('skin_sd_bgm', int(request.attribute('skin_sd_bgm')))
|
||||
if 'skin_sd_se' in request.attributes:
|
||||
newprofile.replace_int('skin_sd_se', int(request.attribute('skin_sd_se')))
|
||||
newprofile.replace_int("chara", int(request.attribute("chara_num")))
|
||||
if "option" in request.attributes:
|
||||
newprofile.replace_int("option", int(request.attribute("option")))
|
||||
if "last_play_flag" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"last_play_flag", int(request.attribute("last_play_flag"))
|
||||
)
|
||||
if "medal_and_friend" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"medal_and_friend", int(request.attribute("medal_and_friend"))
|
||||
)
|
||||
if "music_num" in request.attributes:
|
||||
newprofile.replace_int("music", int(request.attribute("music_num")))
|
||||
if "sheet_num" in request.attributes:
|
||||
newprofile.replace_int("sheet", int(request.attribute("sheet_num")))
|
||||
if "category_num" in request.attributes:
|
||||
newprofile.replace_int("category", int(request.attribute("category_num")))
|
||||
if "read_news_no_max" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"read_news", int(request.attribute("read_news_no_max"))
|
||||
)
|
||||
if "jubeat_collabo" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"jubeat_collabo", int(request.attribute("jubeat_collabo"))
|
||||
)
|
||||
if "norma_point" in request.attributes:
|
||||
newprofile.replace_int("norma_point", int(request.attribute("norma_point")))
|
||||
if "skin_tex_note" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"skin_tex_note", int(request.attribute("skin_tex_note"))
|
||||
)
|
||||
if "skin_tex_cmn" in request.attributes:
|
||||
newprofile.replace_int(
|
||||
"skin_tex_cmn", int(request.attribute("skin_tex_cmn"))
|
||||
)
|
||||
if "skin_sd_bgm" in request.attributes:
|
||||
newprofile.replace_int("skin_sd_bgm", int(request.attribute("skin_sd_bgm")))
|
||||
if "skin_sd_se" in request.attributes:
|
||||
newprofile.replace_int("skin_sd_se", int(request.attribute("skin_sd_se")))
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid)
|
||||
|
||||
# Extract scores
|
||||
for node in request.children:
|
||||
if node.name == 'music':
|
||||
songid = int(node.attribute('music_num'))
|
||||
chart = int(node.attribute('sheet_num'))
|
||||
points = int(node.attribute('score'))
|
||||
data = int(node.attribute('data'))
|
||||
if node.name == "music":
|
||||
songid = int(node.attribute("music_num"))
|
||||
chart = int(node.attribute("sheet_num"))
|
||||
points = int(node.attribute("score"))
|
||||
data = int(node.attribute("data"))
|
||||
|
||||
# We never save battle scores
|
||||
if chart in [
|
||||
@ -405,7 +458,10 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
continue
|
||||
|
||||
# Arrange order to be compatible with future mixes
|
||||
if playmode in {self.GAME_PLAY_MODE_CHO_CHALLENGE, self.GAME_PLAY_MODE_TOWN_CHO_CHALLENGE}:
|
||||
if playmode in {
|
||||
self.GAME_PLAY_MODE_CHO_CHALLENGE,
|
||||
self.GAME_PLAY_MODE_TOWN_CHO_CHALLENGE,
|
||||
}:
|
||||
if chart in [
|
||||
self.GAME_CHART_TYPE_5_BUTTON,
|
||||
self.GAME_CHART_TYPE_ENJOY_5_BUTTON,
|
||||
@ -463,26 +519,32 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
self.update_score(userid, songid, chart, points, medal)
|
||||
|
||||
# Update town mode data.
|
||||
town = newprofile.get_dict('town')
|
||||
town = newprofile.get_dict("town")
|
||||
|
||||
# Basic stuff that's in the base node for no reason?
|
||||
if 'tp' in request.attributes:
|
||||
town.replace_int('points', int(request.attribute('tp')))
|
||||
if "tp" in request.attributes:
|
||||
town.replace_int("points", int(request.attribute("tp")))
|
||||
|
||||
# Stuff that is in the town node
|
||||
townnode = request.child('town')
|
||||
townnode = request.child("town")
|
||||
if townnode is not None:
|
||||
if 'play_type' in townnode.attributes:
|
||||
town.replace_int('play_type', int(townnode.attribute('play_type')))
|
||||
if 'base' in townnode.attributes:
|
||||
town.replace_int_array('base', 4, [int(x) for x in townnode.attribute('base').split(',')])
|
||||
if 'bought_flg' in townnode.attributes:
|
||||
bought_array = [int(x) for x in townnode.attribute('bought_flg').split(',')]
|
||||
if "play_type" in townnode.attributes:
|
||||
town.replace_int("play_type", int(townnode.attribute("play_type")))
|
||||
if "base" in townnode.attributes:
|
||||
town.replace_int_array(
|
||||
"base", 4, [int(x) for x in townnode.attribute("base").split(",")]
|
||||
)
|
||||
if "bought_flg" in townnode.attributes:
|
||||
bought_array = [
|
||||
int(x) for x in townnode.attribute("bought_flg").split(",")
|
||||
]
|
||||
if len(bought_array) == 3:
|
||||
game_config = self.get_game_config()
|
||||
force_unlock_songs = game_config.get_bool('force_unlock_songs')
|
||||
force_unlock_customizations = game_config.get_bool('force_unlock_customizations')
|
||||
old_bought_array = town.get_int_array('bought_flg', 3)
|
||||
force_unlock_songs = game_config.get_bool("force_unlock_songs")
|
||||
force_unlock_customizations = game_config.get_bool(
|
||||
"force_unlock_customizations"
|
||||
)
|
||||
old_bought_array = town.get_int_array("bought_flg", 3)
|
||||
|
||||
if force_unlock_songs:
|
||||
# Don't save force unlocked flags, it'll clobber the profile.
|
||||
@ -491,66 +553,91 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
# Don't save force unlocked flags, it'll clobber the profile.
|
||||
bought_array[1] = old_bought_array[1]
|
||||
|
||||
town.replace_int_array('bought_flg', 3, bought_array)
|
||||
if 'build_flg' in townnode.attributes:
|
||||
town.replace_int_array('build_flg', 8, [int(x) for x in townnode.attribute('build_flg').split(',')])
|
||||
if 'chara_flg' in townnode.attributes:
|
||||
town.replace_int_array('chara_flg', 19, [int(x) for x in townnode.attribute('chara_flg').split(',')])
|
||||
if 'event_flg' in townnode.attributes:
|
||||
town.replace_int_array('event_flg', 4, [int(x) for x in townnode.attribute('event_flg').split(',')])
|
||||
town.replace_int_array("bought_flg", 3, bought_array)
|
||||
if "build_flg" in townnode.attributes:
|
||||
town.replace_int_array(
|
||||
"build_flg",
|
||||
8,
|
||||
[int(x) for x in townnode.attribute("build_flg").split(",")],
|
||||
)
|
||||
if "chara_flg" in townnode.attributes:
|
||||
town.replace_int_array(
|
||||
"chara_flg",
|
||||
19,
|
||||
[int(x) for x in townnode.attribute("chara_flg").split(",")],
|
||||
)
|
||||
if "event_flg" in townnode.attributes:
|
||||
town.replace_int_array(
|
||||
"event_flg",
|
||||
4,
|
||||
[int(x) for x in townnode.attribute("event_flg").split(",")],
|
||||
)
|
||||
for bid in range(8):
|
||||
if f'building_{bid}' in townnode.attributes:
|
||||
town.replace_int_array(f'building_{bid}', 8, [int(x) for x in townnode.attribute(f'building_{bid}').split(',')])
|
||||
if f"building_{bid}" in townnode.attributes:
|
||||
town.replace_int_array(
|
||||
f"building_{bid}",
|
||||
8,
|
||||
[
|
||||
int(x)
|
||||
for x in townnode.attribute(f"building_{bid}").split(",")
|
||||
],
|
||||
)
|
||||
|
||||
newprofile.replace_dict('town', town)
|
||||
newprofile.replace_dict("town", town)
|
||||
|
||||
return newprofile
|
||||
|
||||
def handle_game_get_request(self, request: Node) -> Node:
|
||||
game_config = self.get_game_config()
|
||||
game_phase = game_config.get_int('game_phase')
|
||||
town_phase = game_config.get_int('town_phase')
|
||||
game_phase = game_config.get_int("game_phase")
|
||||
town_phase = game_config.get_int("town_phase")
|
||||
|
||||
root = Node.void('game')
|
||||
root.set_attribute('game_phase', str(game_phase)) # Phase unlocks, for song availability.
|
||||
root.set_attribute('boss_battle_point', '1')
|
||||
root.set_attribute('boss_diff', '100,100,100,100,100,100,100,100,100,100')
|
||||
root.set_attribute('card_phase', '3')
|
||||
root.set_attribute('event_phase', str(town_phase)) # Town mode, for the main event.
|
||||
root.set_attribute('gfdm_phase', '2')
|
||||
root.set_attribute('ir_phase', '14')
|
||||
root.set_attribute('jubeat_phase', '2')
|
||||
root.set_attribute('local_matching_enable', '1')
|
||||
root.set_attribute('matching_sec', '120')
|
||||
root.set_attribute('netvs_phase', '0') # Net taisen mode phase, maximum 18 (no lobby support).
|
||||
root = Node.void("game")
|
||||
root.set_attribute(
|
||||
"game_phase", str(game_phase)
|
||||
) # Phase unlocks, for song availability.
|
||||
root.set_attribute("boss_battle_point", "1")
|
||||
root.set_attribute("boss_diff", "100,100,100,100,100,100,100,100,100,100")
|
||||
root.set_attribute("card_phase", "3")
|
||||
root.set_attribute(
|
||||
"event_phase", str(town_phase)
|
||||
) # Town mode, for the main event.
|
||||
root.set_attribute("gfdm_phase", "2")
|
||||
root.set_attribute("ir_phase", "14")
|
||||
root.set_attribute("jubeat_phase", "2")
|
||||
root.set_attribute("local_matching_enable", "1")
|
||||
root.set_attribute("matching_sec", "120")
|
||||
root.set_attribute(
|
||||
"netvs_phase", "0"
|
||||
) # Net taisen mode phase, maximum 18 (no lobby support).
|
||||
return root
|
||||
|
||||
def handle_game_active_request(self, request: Node) -> Node:
|
||||
# Update the name of this cab for admin purposes
|
||||
self.update_machine_name(request.attribute('shop_name'))
|
||||
return Node.void('game')
|
||||
self.update_machine_name(request.attribute("shop_name"))
|
||||
return Node.void("game")
|
||||
|
||||
def handle_playerdata_expire_request(self, request: Node) -> Node:
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def handle_playerdata_logout_request(self, request: Node) -> Node:
|
||||
return Node.void('playerdata')
|
||||
return Node.void("playerdata")
|
||||
|
||||
def handle_playerdata_get_request(self, request: Node) -> Node:
|
||||
modelstring = request.attribute('model')
|
||||
refid = request.attribute('ref_id')
|
||||
modelstring = request.attribute("model")
|
||||
refid = request.attribute("ref_id")
|
||||
root = self.get_profile_by_refid(
|
||||
refid,
|
||||
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
||||
)
|
||||
if root is None:
|
||||
root = Node.void('playerdata')
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root = Node.void("playerdata")
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
def handle_playerdata_town_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('ref_id')
|
||||
root = Node.void('playerdata')
|
||||
refid = request.attribute("ref_id")
|
||||
root = Node.void("playerdata")
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is None:
|
||||
@ -560,11 +647,11 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
if profile is None:
|
||||
return root
|
||||
|
||||
town = profile.get_dict('town')
|
||||
town = profile.get_dict("town")
|
||||
|
||||
residence = Node.void('residence')
|
||||
residence = Node.void("residence")
|
||||
root.add_child(residence)
|
||||
residence.set_attribute('id', str(town.get_int('residence')))
|
||||
residence.set_attribute("id", str(town.get_int("residence")))
|
||||
|
||||
# It appears there can be up to 9 map nodes, not sure why. I'm only returning the
|
||||
# first one. Perhaps if there's multiple towns, the residence ID lets you choose
|
||||
@ -572,35 +659,35 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
mapdata = [0] * 180
|
||||
|
||||
# Map over progress for base and buildings. Positions 173-176 are for base flags.
|
||||
base = town.get_int_array('base', 4)
|
||||
base = town.get_int_array("base", 4)
|
||||
for i in range(4):
|
||||
mapdata[173 + i] = base[i]
|
||||
|
||||
# Positions 42-105 are for building flags.
|
||||
for bid, start in enumerate([42, 50, 58, 66, 74, 82, 90, 98]):
|
||||
building = town.get_int_array(f'building_{bid}', 8)
|
||||
building = town.get_int_array(f"building_{bid}", 8)
|
||||
for i in range(8):
|
||||
mapdata[start + i] = building[i]
|
||||
|
||||
mapnode = Node.binary('map', bytes(mapdata))
|
||||
mapnode = Node.binary("map", bytes(mapdata))
|
||||
root.add_child(mapnode)
|
||||
mapnode.set_attribute('residence', '0')
|
||||
mapnode.set_attribute("residence", "0")
|
||||
|
||||
return root
|
||||
|
||||
def handle_playerdata_new_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('ref_id')
|
||||
name = request.attribute('name')
|
||||
refid = request.attribute("ref_id")
|
||||
name = request.attribute("name")
|
||||
root = self.new_profile_by_refid(refid, name)
|
||||
if root is None:
|
||||
root = Node.void('playerdata')
|
||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
||||
root = Node.void("playerdata")
|
||||
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||
return root
|
||||
|
||||
def handle_playerdata_set_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('ref_id')
|
||||
refid = request.attribute("ref_id")
|
||||
|
||||
root = Node.void('playerdata')
|
||||
root = Node.void("playerdata")
|
||||
if refid is None:
|
||||
return root
|
||||
|
||||
@ -608,7 +695,9 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
if userid is None:
|
||||
return root
|
||||
|
||||
oldprofile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
||||
oldprofile = self.get_profile(userid) or Profile(
|
||||
self.game, self.version, refid, 0
|
||||
)
|
||||
newprofile = self.unformat_profile(userid, request, oldprofile)
|
||||
|
||||
if newprofile is not None:
|
||||
@ -618,4 +707,4 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
||||
|
||||
def handle_lobby_requests(self, request: Node) -> Node:
|
||||
# Stub out the entire lobby service
|
||||
return Node.void('lobby')
|
||||
return Node.void("lobby")
|
||||
|
@ -36,76 +36,76 @@ class PopnMusicUsaNeko(PopnMusicModernBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'ints': [
|
||||
"ints": [
|
||||
{
|
||||
'name': 'Music Open Phase',
|
||||
'tip': 'Default music phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'music_phase',
|
||||
'values': {
|
||||
0: 'No music unlocks',
|
||||
1: 'Phase 1',
|
||||
2: 'Phase 2',
|
||||
3: 'Phase 3',
|
||||
4: 'Phase 4',
|
||||
5: 'Phase 5',
|
||||
6: 'Phase 6',
|
||||
7: 'Phase 7',
|
||||
8: 'Phase 8',
|
||||
9: 'Phase 9',
|
||||
10: 'Phase 10',
|
||||
11: 'Phase MAX',
|
||||
}
|
||||
"name": "Music Open Phase",
|
||||
"tip": "Default music phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "music_phase",
|
||||
"values": {
|
||||
0: "No music unlocks",
|
||||
1: "Phase 1",
|
||||
2: "Phase 2",
|
||||
3: "Phase 3",
|
||||
4: "Phase 4",
|
||||
5: "Phase 5",
|
||||
6: "Phase 6",
|
||||
7: "Phase 7",
|
||||
8: "Phase 8",
|
||||
9: "Phase 9",
|
||||
10: "Phase 10",
|
||||
11: "Phase MAX",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'NAVI-Kun Event Phase',
|
||||
'tip': 'NAVI-Kun event phase for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'navikun_phase',
|
||||
'values': {
|
||||
0: 'Phase 1',
|
||||
1: 'Phase 2',
|
||||
2: 'Phase 3',
|
||||
3: 'Phase 4',
|
||||
4: 'Phase 5',
|
||||
5: 'Phase 6',
|
||||
6: 'Phase 7',
|
||||
7: 'Phase 8',
|
||||
8: 'Phase 9',
|
||||
9: 'Phase 10',
|
||||
10: 'Phase 11',
|
||||
11: 'Phase 12',
|
||||
12: 'Phase 13',
|
||||
13: 'Phase 14',
|
||||
14: 'Phase 15',
|
||||
15: 'Phase MAX',
|
||||
"name": "NAVI-Kun Event Phase",
|
||||
"tip": "NAVI-Kun event phase for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "navikun_phase",
|
||||
"values": {
|
||||
0: "Phase 1",
|
||||
1: "Phase 2",
|
||||
2: "Phase 3",
|
||||
3: "Phase 4",
|
||||
4: "Phase 5",
|
||||
5: "Phase 6",
|
||||
6: "Phase 7",
|
||||
7: "Phase 8",
|
||||
8: "Phase 9",
|
||||
9: "Phase 10",
|
||||
10: "Phase 11",
|
||||
11: "Phase 12",
|
||||
12: "Phase 13",
|
||||
13: "Phase 14",
|
||||
14: "Phase 15",
|
||||
15: "Phase MAX",
|
||||
},
|
||||
},
|
||||
{
|
||||
# For festive times, it's possible to change the welcome greeting. I'm not sure why you would want to change this, but now you can.
|
||||
'name': 'Holiday Greeting',
|
||||
'tip': 'Changes the payment selection confirmation sound.',
|
||||
'category': 'game_config',
|
||||
'setting': 'holiday_greeting',
|
||||
'values': {
|
||||
0: 'Okay!',
|
||||
1: 'Merry Christmas!',
|
||||
2: 'Happy New Year!',
|
||||
}
|
||||
"name": "Holiday Greeting",
|
||||
"tip": "Changes the payment selection confirmation sound.",
|
||||
"category": "game_config",
|
||||
"setting": "holiday_greeting",
|
||||
"values": {
|
||||
0: "Okay!",
|
||||
1: "Merry Christmas!",
|
||||
2: "Happy New Year!",
|
||||
},
|
||||
},
|
||||
{
|
||||
'name': 'Active Event',
|
||||
'tip': 'Active event for all players.',
|
||||
'category': 'game_config',
|
||||
'setting': 'active_event',
|
||||
'values': {
|
||||
0: 'No event',
|
||||
1: 'NAVI-Kun event',
|
||||
2: 'Daily Mission event',
|
||||
"name": "Active Event",
|
||||
"tip": "Active event for all players.",
|
||||
"category": "game_config",
|
||||
"setting": "active_event",
|
||||
"values": {
|
||||
0: "No event",
|
||||
1: "NAVI-Kun event",
|
||||
2: "Daily Mission event",
|
||||
},
|
||||
},
|
||||
],
|
||||
'bools': [
|
||||
"bools": [
|
||||
# We don't currently support lobbies or anything, so this is commented out until
|
||||
# somebody gets around to implementing it.
|
||||
# {
|
||||
@ -115,20 +115,20 @@ class PopnMusicUsaNeko(PopnMusicModernBase):
|
||||
# 'setting': 'enable_net_taisen',
|
||||
# },
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||
game_config = self.get_game_config()
|
||||
music_phase = game_config.get_int('music_phase')
|
||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
||||
active_event = game_config.get_int('active_event')
|
||||
navikun_phase = game_config.get_int('navikun_phase')
|
||||
music_phase = game_config.get_int("music_phase")
|
||||
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||
active_event = game_config.get_int("active_event")
|
||||
navikun_phase = game_config.get_int("navikun_phase")
|
||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||||
|
||||
navikun_enabled = active_event == 1
|
||||
|
@ -26,23 +26,31 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
CLEAR_TYPE_NO_PLAY: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
|
||||
CLEAR_TYPE_FAILED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
|
||||
CLEAR_TYPE_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED
|
||||
CLEAR_TYPE_HARD_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED
|
||||
CLEAR_TYPE_S_HARD_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED
|
||||
CLEAR_TYPE_HARD_CLEARED: Final[
|
||||
int
|
||||
] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED
|
||||
CLEAR_TYPE_S_HARD_CLEARED: Final[
|
||||
int
|
||||
] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED
|
||||
|
||||
# Combo types, as saved/loaded from the DB
|
||||
COMBO_TYPE_NONE: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE
|
||||
COMBO_TYPE_ALMOST_COMBO: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO
|
||||
COMBO_TYPE_ALMOST_COMBO: Final[
|
||||
int
|
||||
] = DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO
|
||||
COMBO_TYPE_FULL_COMBO: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO
|
||||
COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST
|
||||
COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[
|
||||
int
|
||||
] = DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST
|
||||
|
||||
# Return the local2 and lobby2 service so that matching will work on newer
|
||||
# Reflec Beat games.
|
||||
extra_services: List[str] = [
|
||||
'local2',
|
||||
'lobby2',
|
||||
"local2",
|
||||
"lobby2",
|
||||
]
|
||||
|
||||
def previous_version(self) -> Optional['ReflecBeatBase']:
|
||||
def previous_version(self) -> Optional["ReflecBeatBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -54,9 +62,11 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('pc')
|
||||
return Node.void("pc")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -84,7 +94,9 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
def put_profile_by_refid(self, refid: Optional[str], request: Node) -> Optional[Profile]:
|
||||
def put_profile_by_refid(
|
||||
self, refid: Optional[str], request: Node
|
||||
) -> Optional[Profile]:
|
||||
"""
|
||||
Given a RefID and a request node, unformat the profile and save it.
|
||||
"""
|
||||
@ -121,10 +133,10 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
clear_type: int,
|
||||
combo_type: int,
|
||||
miss_count: int,
|
||||
combo: Optional[int]=None,
|
||||
stats: Optional[Dict[str, int]]=None,
|
||||
param: Optional[int]=None,
|
||||
kflag: Optional[int]=None,
|
||||
combo: Optional[int] = None,
|
||||
stats: Optional[Dict[str, int]] = None,
|
||||
param: Optional[int] = None,
|
||||
kflag: Optional[int] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -176,53 +188,66 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Update the last played time
|
||||
scoredata.replace_int('last_played_time', now)
|
||||
scoredata.replace_int("last_played_time", now)
|
||||
|
||||
# Replace clear type with highest value and timestamps
|
||||
if clear_type >= scoredata.get_int('clear_type'):
|
||||
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
|
||||
scoredata.replace_int('best_clear_type_time', now)
|
||||
history.replace_int('clear_type', clear_type)
|
||||
if clear_type >= scoredata.get_int("clear_type"):
|
||||
scoredata.replace_int(
|
||||
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||
)
|
||||
scoredata.replace_int("best_clear_type_time", now)
|
||||
history.replace_int("clear_type", clear_type)
|
||||
|
||||
# Replace combo type with highest value and timestamps
|
||||
if combo_type >= scoredata.get_int('combo_type'):
|
||||
scoredata.replace_int('combo_type', max(scoredata.get_int('combo_type'), combo_type))
|
||||
scoredata.replace_int('best_clear_type_time', now)
|
||||
history.replace_int('combo_type', combo_type)
|
||||
if combo_type >= scoredata.get_int("combo_type"):
|
||||
scoredata.replace_int(
|
||||
"combo_type", max(scoredata.get_int("combo_type"), combo_type)
|
||||
)
|
||||
scoredata.replace_int("best_clear_type_time", now)
|
||||
history.replace_int("combo_type", combo_type)
|
||||
|
||||
# Update the combo for this song
|
||||
if combo is not None:
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
history.replace_int("combo", combo)
|
||||
|
||||
# Update the param for this song
|
||||
if param is not None:
|
||||
scoredata.replace_int('param', max(scoredata.get_int('param'), param))
|
||||
history.replace_int('param', param)
|
||||
scoredata.replace_int("param", max(scoredata.get_int("param"), param))
|
||||
history.replace_int("param", param)
|
||||
|
||||
# Update the kflag for this song
|
||||
if kflag is not None:
|
||||
scoredata.replace_int('kflag', max(scoredata.get_int('kflag'), kflag))
|
||||
history.replace_int('kflag', kflag)
|
||||
scoredata.replace_int("kflag", max(scoredata.get_int("kflag"), kflag))
|
||||
history.replace_int("kflag", kflag)
|
||||
|
||||
# Update win/lost/draw stats for this song
|
||||
if stats is not None:
|
||||
scoredata.replace_dict('stats', stats)
|
||||
history.replace_dict('stats', stats)
|
||||
scoredata.replace_dict("stats", stats)
|
||||
history.replace_dict("stats", stats)
|
||||
|
||||
# Update the achievement rate with timestamps
|
||||
if achievement_rate >= scoredata.get_int('achievement_rate'):
|
||||
scoredata.replace_int('achievement_rate', max(scoredata.get_int('achievement_rate'), achievement_rate))
|
||||
scoredata.replace_int('best_achievement_rate_time', now)
|
||||
history.replace_int('achievement_rate', achievement_rate)
|
||||
if achievement_rate >= scoredata.get_int("achievement_rate"):
|
||||
scoredata.replace_int(
|
||||
"achievement_rate",
|
||||
max(scoredata.get_int("achievement_rate"), achievement_rate),
|
||||
)
|
||||
scoredata.replace_int("best_achievement_rate_time", now)
|
||||
history.replace_int("achievement_rate", achievement_rate)
|
||||
|
||||
# Update the miss count with timestamps, either if it was lowered, or if the old value was blank.
|
||||
# If the new value is -1 (we didn't get a miss count this time), never update the old value.
|
||||
if miss_count >= 0:
|
||||
if miss_count <= scoredata.get_int('miss_count', 999999) or scoredata.get_int('miss_count') == -1:
|
||||
scoredata.replace_int('miss_count', min(scoredata.get_int('miss_count', 999999), miss_count))
|
||||
scoredata.replace_int('best_miss_count_time', now)
|
||||
history.replace_int('miss_count', miss_count)
|
||||
if (
|
||||
miss_count <= scoredata.get_int("miss_count", 999999)
|
||||
or scoredata.get_int("miss_count") == -1
|
||||
):
|
||||
scoredata.replace_int(
|
||||
"miss_count",
|
||||
min(scoredata.get_int("miss_count", 999999), miss_count),
|
||||
)
|
||||
scoredata.replace_int("best_miss_count_time", now)
|
||||
history.replace_int("miss_count", miss_count)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,12 +24,17 @@ class ReflecBeatFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['KBR', 'LBR', 'MBR']:
|
||||
for gamecode in ["KBR", "LBR", "MBR"]:
|
||||
Base.register(gamecode, ReflecBeatFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date < 2014060400:
|
||||
return VersionConstants.REFLEC_BEAT_COLETTE
|
||||
@ -43,16 +48,16 @@ class ReflecBeatFactory(Factory):
|
||||
return VersionConstants.REFLEC_BEAT_REFLESIA
|
||||
return None
|
||||
|
||||
if model.gamecode == 'KBR':
|
||||
if model.gamecode == "KBR":
|
||||
return ReflecBeat(data, config, model)
|
||||
if model.gamecode == 'LBR':
|
||||
if model.gamecode == "LBR":
|
||||
return ReflecBeatLimelight(data, config, model)
|
||||
if model.gamecode == 'MBR':
|
||||
if model.gamecode == "MBR":
|
||||
if model.version is None:
|
||||
if parentmodel is None:
|
||||
return None
|
||||
|
||||
if parentmodel.gamecode not in ['KBR', 'LBR', 'MBR']:
|
||||
if parentmodel.gamecode not in ["KBR", "LBR", "MBR"]:
|
||||
return None
|
||||
parentversion = version_from_date(parentmodel.version)
|
||||
if parentversion == VersionConstants.REFLEC_BEAT_COLETTE:
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -27,15 +27,15 @@ class ReflecBeat(ReflecBeatBase):
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
],
|
||||
'ints': [],
|
||||
"ints": [],
|
||||
}
|
||||
|
||||
def __db_to_game_clear_type(self, db_clear_type: int, db_combo_type: int) -> int:
|
||||
@ -59,10 +59,12 @@ class ReflecBeat(ReflecBeatBase):
|
||||
]:
|
||||
return self.GAME_CLEAR_TYPE_FULL_COMBO
|
||||
|
||||
raise Exception(f'Invalid db_combo_type {db_combo_type}')
|
||||
raise Exception(f'Invalid db_clear_type {db_clear_type}')
|
||||
raise Exception(f"Invalid db_combo_type {db_combo_type}")
|
||||
raise Exception(f"Invalid db_clear_type {db_clear_type}")
|
||||
|
||||
def __game_to_db_clear_type(self, game_clear_type: int, game_achievement_rate: int) -> Tuple[int, int]:
|
||||
def __game_to_db_clear_type(
|
||||
self, game_clear_type: int, game_achievement_rate: int
|
||||
) -> Tuple[int, int]:
|
||||
if game_clear_type == self.GAME_CLEAR_TYPE_NO_PLAY:
|
||||
return (self.CLEAR_TYPE_NO_PLAY, self.COMBO_TYPE_NONE)
|
||||
if game_clear_type == self.GAME_CLEAR_TYPE_PLAYED:
|
||||
@ -73,67 +75,69 @@ class ReflecBeat(ReflecBeatBase):
|
||||
if game_clear_type == self.GAME_CLEAR_TYPE_FULL_COMBO:
|
||||
return (self.CLEAR_TYPE_CLEARED, self.COMBO_TYPE_FULL_COMBO)
|
||||
|
||||
raise Exception(f'Invalid game_clear_type {game_clear_type}')
|
||||
raise Exception(f"Invalid game_clear_type {game_clear_type}")
|
||||
|
||||
def handle_log_pcb_status_request(self, request: Node) -> Node:
|
||||
return Node.void('log')
|
||||
return Node.void("log")
|
||||
|
||||
def handle_log_opsetting_request(self, request: Node) -> Node:
|
||||
return Node.void('log')
|
||||
return Node.void("log")
|
||||
|
||||
def handle_log_play_request(self, request: Node) -> Node:
|
||||
return Node.void('log')
|
||||
return Node.void("log")
|
||||
|
||||
def handle_pcbinfo_get_request(self, request: Node) -> Node:
|
||||
shop_id = ID.parse_machine_id(request.child_value('lid'))
|
||||
shop_id = ID.parse_machine_id(request.child_value("lid"))
|
||||
machine = self.get_machine_by_id(shop_id)
|
||||
if machine is not None:
|
||||
machine_name = machine.name
|
||||
close = machine.data.get_bool('close')
|
||||
hour = machine.data.get_int('hour')
|
||||
minute = machine.data.get_int('minute')
|
||||
pref = machine.data.get_int('pref', self.get_machine_region())
|
||||
close = machine.data.get_bool("close")
|
||||
hour = machine.data.get_int("hour")
|
||||
minute = machine.data.get_int("minute")
|
||||
pref = machine.data.get_int("pref", self.get_machine_region())
|
||||
else:
|
||||
machine_name = ''
|
||||
machine_name = ""
|
||||
close = False
|
||||
hour = 0
|
||||
minute = 0
|
||||
pref = self.get_machine_region()
|
||||
|
||||
root = Node.void('pcbinfo')
|
||||
info = Node.void('info')
|
||||
root = Node.void("pcbinfo")
|
||||
info = Node.void("info")
|
||||
root.add_child(info)
|
||||
|
||||
info.add_child(Node.string('name', machine_name))
|
||||
info.add_child(Node.s16('pref', pref))
|
||||
info.add_child(Node.bool('close', close))
|
||||
info.add_child(Node.u8('hour', hour))
|
||||
info.add_child(Node.u8('min', minute))
|
||||
info.add_child(Node.string("name", machine_name))
|
||||
info.add_child(Node.s16("pref", pref))
|
||||
info.add_child(Node.bool("close", close))
|
||||
info.add_child(Node.u8("hour", hour))
|
||||
info.add_child(Node.u8("min", minute))
|
||||
|
||||
return root
|
||||
|
||||
def handle_pcbinfo_set_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.child_value('info/name'))
|
||||
self.update_machine_data({
|
||||
'close': request.child_value('info/close'),
|
||||
'hour': request.child_value('info/hour'),
|
||||
'minute': request.child_value('info/min'),
|
||||
'pref': request.child_value('info/pref'),
|
||||
})
|
||||
return Node.void('pcbinfo')
|
||||
self.update_machine_name(request.child_value("info/name"))
|
||||
self.update_machine_data(
|
||||
{
|
||||
"close": request.child_value("info/close"),
|
||||
"hour": request.child_value("info/hour"),
|
||||
"minute": request.child_value("info/min"),
|
||||
"pref": request.child_value("info/pref"),
|
||||
}
|
||||
)
|
||||
return Node.void("pcbinfo")
|
||||
|
||||
def __add_event_info(self, request: Node) -> None:
|
||||
events: Dict[int, int] = {}
|
||||
|
||||
for (_eventid, _phase) in events.items():
|
||||
data = Node.void('data')
|
||||
data = Node.void("data")
|
||||
request.add_child(data)
|
||||
data.add_child(Node.s32('type', -1))
|
||||
data.add_child(Node.s32('value', -1))
|
||||
data.add_child(Node.s32("type", -1))
|
||||
data.add_child(Node.s32("value", -1))
|
||||
|
||||
def handle_sysinfo_get_request(self, request: Node) -> Node:
|
||||
root = Node.void('sysinfo')
|
||||
trd = Node.void('trd')
|
||||
root = Node.void("sysinfo")
|
||||
trd = Node.void("trd")
|
||||
root.add_child(trd)
|
||||
|
||||
# Add event info
|
||||
@ -142,16 +146,16 @@ class ReflecBeat(ReflecBeatBase):
|
||||
return root
|
||||
|
||||
def handle_sysinfo_fan_request(self, request: Node) -> Node:
|
||||
sysinfo = Node.void('sysinfo')
|
||||
sysinfo.add_child(Node.u8('pref', self.get_machine_region()))
|
||||
sysinfo.add_child(Node.string('lid', request.child_value('lid')))
|
||||
sysinfo = Node.void("sysinfo")
|
||||
sysinfo.add_child(Node.u8("pref", self.get_machine_region()))
|
||||
sysinfo.add_child(Node.string("lid", request.child_value("lid")))
|
||||
return sysinfo
|
||||
|
||||
def handle_lobby_entry_request(self, request: Node) -> Node:
|
||||
root = Node.void('lobby')
|
||||
root = Node.void("lobby")
|
||||
|
||||
# Create a lobby entry for this user
|
||||
extid = request.child_value('e/uid')
|
||||
extid = request.child_value("e/uid")
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid)
|
||||
@ -160,49 +164,49 @@ class ReflecBeat(ReflecBeatBase):
|
||||
self.version,
|
||||
userid,
|
||||
{
|
||||
'mid': request.child_value('e/mid'),
|
||||
'ng': request.child_value('e/ng'),
|
||||
'lid': request.child_value('e/lid'),
|
||||
'sn': request.child_value('e/sn'),
|
||||
'pref': request.child_value('e/pref'),
|
||||
'ga': request.child_value('e/ga'),
|
||||
'gp': request.child_value('e/gp'),
|
||||
'la': request.child_value('e/la'),
|
||||
}
|
||||
"mid": request.child_value("e/mid"),
|
||||
"ng": request.child_value("e/ng"),
|
||||
"lid": request.child_value("e/lid"),
|
||||
"sn": request.child_value("e/sn"),
|
||||
"pref": request.child_value("e/pref"),
|
||||
"ga": request.child_value("e/ga"),
|
||||
"gp": request.child_value("e/gp"),
|
||||
"la": request.child_value("e/la"),
|
||||
},
|
||||
)
|
||||
lobby = self.data.local.lobby.get_lobby(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
)
|
||||
root.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e = Node.void('e')
|
||||
root.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e = Node.void("e")
|
||||
root.add_child(e)
|
||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
||||
e.add_child(Node.s32('uid', profile.extid))
|
||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
||||
e.add_child(Node.s32('exp', profile.get_int('exp')))
|
||||
e.add_child(Node.u8('mg', profile.get_int('mg')))
|
||||
e.add_child(Node.s32('tid', lobby.get_int('tid')))
|
||||
e.add_child(Node.string('tn', lobby.get_str('tn')))
|
||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
||||
e.add_child(Node.u8('pref', lobby.get_int('pref')))
|
||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
||||
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||
e.add_child(Node.s32("uid", profile.extid))
|
||||
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||
e.add_child(Node.s32("exp", profile.get_int("exp")))
|
||||
e.add_child(Node.u8("mg", profile.get_int("mg")))
|
||||
e.add_child(Node.s32("tid", lobby.get_int("tid")))
|
||||
e.add_child(Node.string("tn", lobby.get_str("tn")))
|
||||
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||
e.add_child(Node.string("sn", lobby.get_str("sn")))
|
||||
e.add_child(Node.u8("pref", lobby.get_int("pref")))
|
||||
e.add_child(Node.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||
e.add_child(Node.u16("gp", lobby.get_int("gp")))
|
||||
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||
|
||||
return root
|
||||
|
||||
def handle_lobby_read_request(self, request: Node) -> Node:
|
||||
root = Node.void('lobby')
|
||||
root = Node.void("lobby")
|
||||
|
||||
# Look up all lobbies matching the criteria specified
|
||||
mg = request.child_value('m_grade') # noqa: F841
|
||||
extid = request.child_value('uid')
|
||||
limit = request.child_value('max')
|
||||
mg = request.child_value("m_grade") # noqa: F841
|
||||
extid = request.child_value("uid")
|
||||
limit = request.child_value("max")
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
if userid is not None:
|
||||
lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version)
|
||||
@ -219,47 +223,44 @@ class ReflecBeat(ReflecBeatBase):
|
||||
# No profile info, don't return this lobby
|
||||
continue
|
||||
|
||||
e = Node.void('e')
|
||||
e = Node.void("e")
|
||||
root.add_child(e)
|
||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
||||
e.add_child(Node.s32('uid', profile.extid))
|
||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
||||
e.add_child(Node.s32('exp', profile.get_int('exp')))
|
||||
e.add_child(Node.u8('mg', profile.get_int('mg')))
|
||||
e.add_child(Node.s32('tid', lobby.get_int('tid')))
|
||||
e.add_child(Node.string('tn', lobby.get_str('tn')))
|
||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
||||
e.add_child(Node.u8('pref', lobby.get_int('pref')))
|
||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
||||
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||
e.add_child(Node.s32("uid", profile.extid))
|
||||
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||
e.add_child(Node.s32("exp", profile.get_int("exp")))
|
||||
e.add_child(Node.u8("mg", profile.get_int("mg")))
|
||||
e.add_child(Node.s32("tid", lobby.get_int("tid")))
|
||||
e.add_child(Node.string("tn", lobby.get_str("tn")))
|
||||
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||
e.add_child(Node.string("sn", lobby.get_str("sn")))
|
||||
e.add_child(Node.u8("pref", lobby.get_int("pref")))
|
||||
e.add_child(Node.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||
e.add_child(Node.u16("gp", lobby.get_int("gp")))
|
||||
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||
|
||||
limit = limit - 1
|
||||
|
||||
return root
|
||||
|
||||
def handle_lobby_delete_request(self, request: Node) -> Node:
|
||||
eid = request.child_value('eid')
|
||||
eid = request.child_value("eid")
|
||||
self.data.local.lobby.destroy_lobby(eid)
|
||||
return Node.void('lobby')
|
||||
return Node.void("lobby")
|
||||
|
||||
def handle_player_start_request(self, request: Node) -> Node:
|
||||
# Add a dummy entry into the lobby setup so we can clean up on end play
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
self.data.local.lobby.put_play_session_info(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
{}
|
||||
self.game, self.version, userid, {}
|
||||
)
|
||||
|
||||
root = Node.void('player')
|
||||
root.add_child(Node.bool('is_suc', True))
|
||||
root = Node.void("player")
|
||||
root.add_child(Node.bool("is_suc", True))
|
||||
|
||||
# Add event info
|
||||
self.__add_event_info(root)
|
||||
@ -267,11 +268,11 @@ class ReflecBeat(ReflecBeatBase):
|
||||
return root
|
||||
|
||||
def handle_player_delete_request(self, request: Node) -> Node:
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
||||
def handle_player_end_request(self, request: Node) -> Node:
|
||||
# Destroy play session based on info from the request
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
# Kill any lingering lobbies by this user
|
||||
@ -281,159 +282,182 @@ class ReflecBeat(ReflecBeatBase):
|
||||
userid,
|
||||
)
|
||||
if lobby is not None:
|
||||
self.data.local.lobby.destroy_lobby(lobby.get_int('id'))
|
||||
self.data.local.lobby.destroy_play_session_info(self.game, self.version, userid)
|
||||
self.data.local.lobby.destroy_lobby(lobby.get_int("id"))
|
||||
self.data.local.lobby.destroy_play_session_info(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
||||
def handle_player_read_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
profile = self.get_profile_by_refid(refid)
|
||||
if profile:
|
||||
return profile
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
||||
def handle_player_write_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
profile = self.put_profile_by_refid(refid, request)
|
||||
root = Node.void('player')
|
||||
root = Node.void("player")
|
||||
|
||||
if profile is None:
|
||||
root.add_child(Node.s32('uid', 0))
|
||||
root.add_child(Node.s32("uid", 0))
|
||||
else:
|
||||
root.add_child(Node.s32('uid', profile.extid))
|
||||
root.add_child(Node.s32('time', Time.now()))
|
||||
root.add_child(Node.s32("uid", profile.extid))
|
||||
root.add_child(Node.s32("time", Time.now()))
|
||||
return root
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
statistics = self.get_play_statistics(userid)
|
||||
game_config = self.get_game_config()
|
||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
||||
achievements = self.data.local.user.get_achievements(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
scores = self.data.remote.music.get_scores(self.game, self.version, userid)
|
||||
root = Node.void('player')
|
||||
pdata = Node.void('pdata')
|
||||
root = Node.void("player")
|
||||
pdata = Node.void("pdata")
|
||||
root.add_child(pdata)
|
||||
|
||||
base = Node.void('base')
|
||||
base = Node.void("base")
|
||||
pdata.add_child(base)
|
||||
base.add_child(Node.s32('uid', profile.extid))
|
||||
base.add_child(Node.string('name', profile.get_str('name')))
|
||||
base.add_child(Node.s16('lv', profile.get_int('lvl')))
|
||||
base.add_child(Node.s32('exp', profile.get_int('exp')))
|
||||
base.add_child(Node.s16('mg', profile.get_int('mg')))
|
||||
base.add_child(Node.s16('ap', profile.get_int('ap')))
|
||||
base.add_child(Node.s32('flag', profile.get_int('flag')))
|
||||
base.add_child(Node.s32("uid", profile.extid))
|
||||
base.add_child(Node.string("name", profile.get_str("name")))
|
||||
base.add_child(Node.s16("lv", profile.get_int("lvl")))
|
||||
base.add_child(Node.s32("exp", profile.get_int("exp")))
|
||||
base.add_child(Node.s16("mg", profile.get_int("mg")))
|
||||
base.add_child(Node.s16("ap", profile.get_int("ap")))
|
||||
base.add_child(Node.s32("flag", profile.get_int("flag")))
|
||||
|
||||
con = Node.void('con')
|
||||
con = Node.void("con")
|
||||
pdata.add_child(con)
|
||||
con.add_child(Node.s32('day', statistics.today_plays))
|
||||
con.add_child(Node.s32('cnt', statistics.total_plays))
|
||||
con.add_child(Node.s32('last', statistics.last_play_timestamp))
|
||||
con.add_child(Node.s32('now', Time.now()))
|
||||
con.add_child(Node.s32("day", statistics.today_plays))
|
||||
con.add_child(Node.s32("cnt", statistics.total_plays))
|
||||
con.add_child(Node.s32("last", statistics.last_play_timestamp))
|
||||
con.add_child(Node.s32("now", Time.now()))
|
||||
|
||||
team = Node.void('team')
|
||||
team = Node.void("team")
|
||||
pdata.add_child(team)
|
||||
team.add_child(Node.s32('id', -1))
|
||||
team.add_child(Node.string('name', ''))
|
||||
team.add_child(Node.s32("id", -1))
|
||||
team.add_child(Node.string("name", ""))
|
||||
|
||||
custom = Node.void('custom')
|
||||
customdict = profile.get_dict('custom')
|
||||
custom = Node.void("custom")
|
||||
customdict = profile.get_dict("custom")
|
||||
pdata.add_child(custom)
|
||||
custom.add_child(Node.u8('bgm_m', customdict.get_int('bgm_m')))
|
||||
custom.add_child(Node.u8('st_f', customdict.get_int('st_f')))
|
||||
custom.add_child(Node.u8('st_bg', customdict.get_int('st_bg')))
|
||||
custom.add_child(Node.u8('st_bg_b', customdict.get_int('st_bg_b')))
|
||||
custom.add_child(Node.u8('eff_e', customdict.get_int('eff_e')))
|
||||
custom.add_child(Node.u8('se_s', customdict.get_int('se_s')))
|
||||
custom.add_child(Node.u8('se_s_v', customdict.get_int('se_s_v')))
|
||||
custom.add_child(Node.u8("bgm_m", customdict.get_int("bgm_m")))
|
||||
custom.add_child(Node.u8("st_f", customdict.get_int("st_f")))
|
||||
custom.add_child(Node.u8("st_bg", customdict.get_int("st_bg")))
|
||||
custom.add_child(Node.u8("st_bg_b", customdict.get_int("st_bg_b")))
|
||||
custom.add_child(Node.u8("eff_e", customdict.get_int("eff_e")))
|
||||
custom.add_child(Node.u8("se_s", customdict.get_int("se_s")))
|
||||
custom.add_child(Node.u8("se_s_v", customdict.get_int("se_s_v")))
|
||||
|
||||
released = Node.void('released')
|
||||
released = Node.void("released")
|
||||
pdata.add_child(released)
|
||||
|
||||
for item in achievements:
|
||||
if item.type[:5] != 'item_':
|
||||
if item.type[:5] != "item_":
|
||||
continue
|
||||
itemtype = int(item.type[5:])
|
||||
if game_config.get_bool('force_unlock_songs') and itemtype == 0:
|
||||
if game_config.get_bool("force_unlock_songs") and itemtype == 0:
|
||||
# Don't echo unlocks when we're force unlocking, we'll do it later
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
released.add_child(info)
|
||||
info.add_child(Node.u8('type', itemtype))
|
||||
info.add_child(Node.u16('id', item.id))
|
||||
info.add_child(Node.u8("type", itemtype))
|
||||
info.add_child(Node.u16("id", item.id))
|
||||
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
songs = {song.id for song in self.data.local.music.get_all_songs(self.game, self.version)}
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
songs = {
|
||||
song.id
|
||||
for song in self.data.local.music.get_all_songs(self.game, self.version)
|
||||
}
|
||||
|
||||
for songid in songs:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
released.add_child(info)
|
||||
info.add_child(Node.u8('type', 0))
|
||||
info.add_child(Node.u16('id', songid))
|
||||
info.add_child(Node.u8("type", 0))
|
||||
info.add_child(Node.u16("id", songid))
|
||||
|
||||
# Scores
|
||||
record = Node.void('record')
|
||||
record = Node.void("record")
|
||||
pdata.add_child(record)
|
||||
|
||||
for score in scores:
|
||||
rec = Node.void('rec')
|
||||
rec = Node.void("rec")
|
||||
record.add_child(rec)
|
||||
rec.add_child(Node.u16('mid', score.id))
|
||||
rec.add_child(Node.u8('ng', score.chart))
|
||||
rec.add_child(Node.s32('win', score.data.get_dict('stats').get_int('win')))
|
||||
rec.add_child(Node.s32('lose', score.data.get_dict('stats').get_int('lose')))
|
||||
rec.add_child(Node.s32('draw', score.data.get_dict('stats').get_int('draw')))
|
||||
rec.add_child(Node.u8('ct', self.__db_to_game_clear_type(score.data.get_int('clear_type'), score.data.get_int('combo_type'))))
|
||||
rec.add_child(Node.s16('ar', int(score.data.get_int('achievement_rate') / 10)))
|
||||
rec.add_child(Node.s16('bs', score.points))
|
||||
rec.add_child(Node.s16('mc', score.data.get_int('combo')))
|
||||
rec.add_child(Node.s16('bmc', score.data.get_int('miss_count')))
|
||||
rec.add_child(Node.u16("mid", score.id))
|
||||
rec.add_child(Node.u8("ng", score.chart))
|
||||
rec.add_child(Node.s32("win", score.data.get_dict("stats").get_int("win")))
|
||||
rec.add_child(
|
||||
Node.s32("lose", score.data.get_dict("stats").get_int("lose"))
|
||||
)
|
||||
rec.add_child(
|
||||
Node.s32("draw", score.data.get_dict("stats").get_int("draw"))
|
||||
)
|
||||
rec.add_child(
|
||||
Node.u8(
|
||||
"ct",
|
||||
self.__db_to_game_clear_type(
|
||||
score.data.get_int("clear_type"),
|
||||
score.data.get_int("combo_type"),
|
||||
),
|
||||
)
|
||||
)
|
||||
rec.add_child(
|
||||
Node.s16("ar", int(score.data.get_int("achievement_rate") / 10))
|
||||
)
|
||||
rec.add_child(Node.s16("bs", score.points))
|
||||
rec.add_child(Node.s16("mc", score.data.get_int("combo")))
|
||||
rec.add_child(Node.s16("bmc", score.data.get_int("miss_count")))
|
||||
|
||||
# In original ReflecBeat, the entire battle log was returned for each battle.
|
||||
# We don't support storing all of that info, so don't return anything here.
|
||||
blog = Node.void('blog')
|
||||
blog = Node.void("blog")
|
||||
pdata.add_child(blog)
|
||||
|
||||
# Comment (seems unused?)
|
||||
pdata.add_child(Node.string('cmnt', ''))
|
||||
pdata.add_child(Node.string("cmnt", ""))
|
||||
|
||||
return root
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
game_config = self.get_game_config()
|
||||
newprofile = oldprofile.clone()
|
||||
|
||||
newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('lid')))
|
||||
newprofile.replace_str('name', request.child_value('pdata/base/name'))
|
||||
newprofile.replace_int('lvl', request.child_value('pdata/base/lv'))
|
||||
newprofile.replace_int('exp', request.child_value('pdata/base/exp'))
|
||||
newprofile.replace_int('mg', request.child_value('pdata/base/mg'))
|
||||
newprofile.replace_int('ap', request.child_value('pdata/base/ap'))
|
||||
newprofile.replace_int('flag', request.child_value('pdata/base/flag'))
|
||||
newprofile.replace_int("lid", ID.parse_machine_id(request.child_value("lid")))
|
||||
newprofile.replace_str("name", request.child_value("pdata/base/name"))
|
||||
newprofile.replace_int("lvl", request.child_value("pdata/base/lv"))
|
||||
newprofile.replace_int("exp", request.child_value("pdata/base/exp"))
|
||||
newprofile.replace_int("mg", request.child_value("pdata/base/mg"))
|
||||
newprofile.replace_int("ap", request.child_value("pdata/base/ap"))
|
||||
newprofile.replace_int("flag", request.child_value("pdata/base/flag"))
|
||||
|
||||
customdict = newprofile.get_dict('custom')
|
||||
custom = request.child('pdata/custom')
|
||||
customdict = newprofile.get_dict("custom")
|
||||
custom = request.child("pdata/custom")
|
||||
if custom:
|
||||
customdict.replace_int('bgm_m', custom.child_value('bgm_m'))
|
||||
customdict.replace_int('st_f', custom.child_value('st_f'))
|
||||
customdict.replace_int('st_bg', custom.child_value('st_bg'))
|
||||
customdict.replace_int('st_bg_b', custom.child_value('st_bg_b'))
|
||||
customdict.replace_int('eff_e', custom.child_value('eff_e'))
|
||||
customdict.replace_int('se_s', custom.child_value('se_s'))
|
||||
customdict.replace_int('se_s_v', custom.child_value('se_s_v'))
|
||||
newprofile.replace_dict('custom', customdict)
|
||||
customdict.replace_int("bgm_m", custom.child_value("bgm_m"))
|
||||
customdict.replace_int("st_f", custom.child_value("st_f"))
|
||||
customdict.replace_int("st_bg", custom.child_value("st_bg"))
|
||||
customdict.replace_int("st_bg_b", custom.child_value("st_bg_b"))
|
||||
customdict.replace_int("eff_e", custom.child_value("eff_e"))
|
||||
customdict.replace_int("se_s", custom.child_value("se_s"))
|
||||
customdict.replace_int("se_s_v", custom.child_value("se_s_v"))
|
||||
newprofile.replace_dict("custom", customdict)
|
||||
|
||||
# Music unlocks and other stuff
|
||||
released = request.child('pdata/released')
|
||||
released = request.child("pdata/released")
|
||||
if released:
|
||||
for child in released.children:
|
||||
if child.name != 'info':
|
||||
if child.name != "info":
|
||||
continue
|
||||
|
||||
item_id = child.child_value('id')
|
||||
item_type = child.child_value('type')
|
||||
if game_config.get_bool('force_unlock_songs') and item_type == 0:
|
||||
item_id = child.child_value("id")
|
||||
item_type = child.child_value("type")
|
||||
if game_config.get_bool("force_unlock_songs") and item_type == 0:
|
||||
# Don't save unlocks when we're force unlocking
|
||||
continue
|
||||
|
||||
@ -442,7 +466,7 @@ class ReflecBeat(ReflecBeatBase):
|
||||
self.version,
|
||||
userid,
|
||||
item_id,
|
||||
f'item_{item_type}',
|
||||
f"item_{item_type}",
|
||||
{},
|
||||
)
|
||||
|
||||
@ -453,46 +477,48 @@ class ReflecBeat(ReflecBeatBase):
|
||||
# the previous try. So, we must also look at the battle log for the actual play scores,
|
||||
# and combine the data if we can.
|
||||
savedrecords: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||
songplays = request.child('pdata/record')
|
||||
songplays = request.child("pdata/record")
|
||||
if songplays:
|
||||
for child in songplays.children:
|
||||
if child.name != 'rec':
|
||||
if child.name != "rec":
|
||||
continue
|
||||
|
||||
songid = child.child_value('mid')
|
||||
chart = child.child_value('ng')
|
||||
songid = child.child_value("mid")
|
||||
chart = child.child_value("ng")
|
||||
|
||||
# These don't get sent with the battle logs, so we try to construct
|
||||
# the values here.
|
||||
if songid not in savedrecords:
|
||||
savedrecords[songid] = {}
|
||||
savedrecords[songid][chart] = {
|
||||
'achievement_rate': child.child_value('ar') * 10,
|
||||
'points': child.child_value('bs'),
|
||||
'combo': child.child_value('mc'),
|
||||
'miss_count': child.child_value('bmc'),
|
||||
'win': child.child_value('win'),
|
||||
'lose': child.child_value('lose'),
|
||||
'draw': child.child_value('draw'),
|
||||
"achievement_rate": child.child_value("ar") * 10,
|
||||
"points": child.child_value("bs"),
|
||||
"combo": child.child_value("mc"),
|
||||
"miss_count": child.child_value("bmc"),
|
||||
"win": child.child_value("win"),
|
||||
"lose": child.child_value("lose"),
|
||||
"draw": child.child_value("draw"),
|
||||
}
|
||||
|
||||
# Now, see the actual battles that were played. If we can, unify the data with a record.
|
||||
# We only do that when the record achievement rate and score matches the battle achievement
|
||||
# rate and score, so we know for a fact that that record was generated by this battle.
|
||||
battlelogs = request.child('pdata/blog')
|
||||
battlelogs = request.child("pdata/blog")
|
||||
if battlelogs:
|
||||
for child in battlelogs.children:
|
||||
if child.name != 'log':
|
||||
if child.name != "log":
|
||||
continue
|
||||
|
||||
songid = child.child_value('mid')
|
||||
chart = child.child_value('ng')
|
||||
songid = child.child_value("mid")
|
||||
chart = child.child_value("ng")
|
||||
|
||||
clear_type = child.child_value('myself/ct')
|
||||
achievement_rate = child.child_value('myself/ar') * 10
|
||||
points = child.child_value('myself/s')
|
||||
clear_type = child.child_value("myself/ct")
|
||||
achievement_rate = child.child_value("myself/ar") * 10
|
||||
points = child.child_value("myself/s")
|
||||
|
||||
clear_type, combo_type = self.__game_to_db_clear_type(clear_type, achievement_rate)
|
||||
clear_type, combo_type = self.__game_to_db_clear_type(
|
||||
clear_type, achievement_rate
|
||||
)
|
||||
|
||||
combo = None
|
||||
miss_count = -1
|
||||
@ -503,17 +529,17 @@ class ReflecBeat(ReflecBeatBase):
|
||||
data = savedrecords[songid][chart]
|
||||
|
||||
if (
|
||||
data['achievement_rate'] == achievement_rate and
|
||||
data['points'] == points
|
||||
data["achievement_rate"] == achievement_rate
|
||||
and data["points"] == points
|
||||
):
|
||||
# This is the same record! Use the stats from it to update our
|
||||
# internal representation.
|
||||
combo = data['combo']
|
||||
miss_count = data['miss_count']
|
||||
combo = data["combo"]
|
||||
miss_count = data["miss_count"]
|
||||
stats = {
|
||||
'win': data['win'],
|
||||
'lose': data['lose'],
|
||||
'draw': data['draw'],
|
||||
"win": data["win"],
|
||||
"lose": data["lose"],
|
||||
"draw": data["draw"],
|
||||
}
|
||||
|
||||
self.update_score(
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -66,33 +66,37 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
return self.COMBO_TYPE_FULL_COMBO
|
||||
if game_combo == self.GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST:
|
||||
return self.COMBO_TYPE_FULL_COMBO_ALL_JUST
|
||||
raise Exception(f'Invalid game_combo value {game_combo}')
|
||||
raise Exception(f"Invalid game_combo value {game_combo}")
|
||||
|
||||
def _add_event_info(self, root: Node) -> None:
|
||||
# Overridden in subclasses
|
||||
pass
|
||||
|
||||
def _add_shop_score(self, root: Node) -> None:
|
||||
shop_score = Node.void('shop_score')
|
||||
shop_score = Node.void("shop_score")
|
||||
root.add_child(shop_score)
|
||||
today = Node.void('today')
|
||||
today = Node.void("today")
|
||||
shop_score.add_child(today)
|
||||
yesterday = Node.void('yesterday')
|
||||
yesterday = Node.void("yesterday")
|
||||
shop_score.add_child(yesterday)
|
||||
|
||||
all_profiles = self.data.local.user.get_all_profiles(self.game, self.version)
|
||||
all_attempts = self.data.local.music.get_all_attempts(self.game, self.version, timelimit=(Time.beginning_of_today() - Time.SECONDS_IN_DAY))
|
||||
all_attempts = self.data.local.music.get_all_attempts(
|
||||
self.game,
|
||||
self.version,
|
||||
timelimit=(Time.beginning_of_today() - Time.SECONDS_IN_DAY),
|
||||
)
|
||||
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||
if machine.arcade is not None:
|
||||
lids = [
|
||||
machine.id for machine in self.data.local.machine.get_all_machines(machine.arcade)
|
||||
machine.id
|
||||
for machine in self.data.local.machine.get_all_machines(machine.arcade)
|
||||
]
|
||||
else:
|
||||
lids = [machine.id]
|
||||
|
||||
relevant_profiles = [
|
||||
profile for profile in all_profiles
|
||||
if profile[1].get_int('lid', -1) in lids
|
||||
profile for profile in all_profiles if profile[1].get_int("lid", -1) in lids
|
||||
]
|
||||
|
||||
for (rootnode, timeoffset) in [
|
||||
@ -101,10 +105,11 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
]:
|
||||
# Grab all attempts made in the relevant day
|
||||
relevant_attempts = [
|
||||
attempt for attempt in all_attempts
|
||||
attempt
|
||||
for attempt in all_attempts
|
||||
if (
|
||||
attempt[1].timestamp >= (Time.beginning_of_today() - timeoffset) and
|
||||
attempt[1].timestamp <= (Time.end_of_today() - timeoffset)
|
||||
attempt[1].timestamp >= (Time.beginning_of_today() - timeoffset)
|
||||
and attempt[1].timestamp <= (Time.end_of_today() - timeoffset)
|
||||
)
|
||||
]
|
||||
|
||||
@ -120,7 +125,10 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
scores_by_user[userid][attempt.id][attempt.chart] = attempt
|
||||
else:
|
||||
# If this attempt is better than the stored one, replace it
|
||||
if scores_by_user[userid][attempt.id][attempt.chart].points < attempt.points:
|
||||
if (
|
||||
scores_by_user[userid][attempt.id][attempt.chart].points
|
||||
< attempt.points
|
||||
):
|
||||
scores_by_user[userid][attempt.id][attempt.chart] = attempt
|
||||
|
||||
# Calculate points earned by user in the day
|
||||
@ -129,52 +137,65 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
points_by_user[userid] = 0
|
||||
for mid in scores_by_user[userid]:
|
||||
for chart in scores_by_user[userid][mid]:
|
||||
points_by_user[userid] = points_by_user[userid] + scores_by_user[userid][mid][chart].points
|
||||
points_by_user[userid] = (
|
||||
points_by_user[userid]
|
||||
+ scores_by_user[userid][mid][chart].points
|
||||
)
|
||||
|
||||
# Output that day's earned points
|
||||
for (userid, profile) in relevant_profiles:
|
||||
data = Node.void('data')
|
||||
data = Node.void("data")
|
||||
rootnode.add_child(data)
|
||||
data.add_child(Node.s16('day_id', int((Time.now() - timeoffset) / Time.SECONDS_IN_DAY)))
|
||||
data.add_child(Node.s32('user_id', profile.extid))
|
||||
data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id')))
|
||||
data.add_child(Node.s16('point', min(points_by_user.get(userid, 0), 32767)))
|
||||
data.add_child(Node.s32('update_time', Time.now()))
|
||||
data.add_child(Node.string('name', profile.get_str('name')))
|
||||
data.add_child(
|
||||
Node.s16(
|
||||
"day_id", int((Time.now() - timeoffset) / Time.SECONDS_IN_DAY)
|
||||
)
|
||||
)
|
||||
data.add_child(Node.s32("user_id", profile.extid))
|
||||
data.add_child(
|
||||
Node.s16("icon_id", profile.get_dict("config").get_int("icon_id"))
|
||||
)
|
||||
data.add_child(
|
||||
Node.s16("point", min(points_by_user.get(userid, 0), 32767))
|
||||
)
|
||||
data.add_child(Node.s32("update_time", Time.now()))
|
||||
data.add_child(Node.string("name", profile.get_str("name")))
|
||||
|
||||
rootnode.add_child(Node.s32('time', Time.beginning_of_today() - timeoffset))
|
||||
rootnode.add_child(Node.s32("time", Time.beginning_of_today() - timeoffset))
|
||||
|
||||
def handle_info_rb5_info_read_request(self, request: Node) -> Node:
|
||||
root = Node.void('info')
|
||||
root = Node.void("info")
|
||||
self._add_event_info(root)
|
||||
|
||||
return root
|
||||
|
||||
def handle_info_rb5_info_read_hit_chart_request(self, request: Node) -> Node:
|
||||
version = request.child_value('ver')
|
||||
version = request.child_value("ver")
|
||||
|
||||
root = Node.void('info')
|
||||
root.add_child(Node.s32('ver', version))
|
||||
ranking = Node.void('ranking')
|
||||
root = Node.void("info")
|
||||
root.add_child(Node.s32("ver", version))
|
||||
ranking = Node.void("ranking")
|
||||
root.add_child(ranking)
|
||||
|
||||
def add_hitchart(name: str, start: int, end: int, hitchart: List[Tuple[int, int]]) -> None:
|
||||
def add_hitchart(
|
||||
name: str, start: int, end: int, hitchart: List[Tuple[int, int]]
|
||||
) -> None:
|
||||
base = Node.void(name)
|
||||
ranking.add_child(base)
|
||||
base.add_child(Node.s32('bt', start))
|
||||
base.add_child(Node.s32('et', end))
|
||||
new = Node.void('new')
|
||||
base.add_child(Node.s32("bt", start))
|
||||
base.add_child(Node.s32("et", end))
|
||||
new = Node.void("new")
|
||||
base.add_child(new)
|
||||
|
||||
for (mid, plays) in hitchart:
|
||||
d = Node.void('d')
|
||||
d = Node.void("d")
|
||||
new.add_child(d)
|
||||
d.add_child(Node.s16('mid', mid))
|
||||
d.add_child(Node.s32('cnt', plays))
|
||||
d.add_child(Node.s16("mid", mid))
|
||||
d.add_child(Node.s32("cnt", plays))
|
||||
|
||||
# Weekly hit chart
|
||||
add_hitchart(
|
||||
'weekly',
|
||||
"weekly",
|
||||
Time.now() - Time.SECONDS_IN_WEEK,
|
||||
Time.now(),
|
||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 7),
|
||||
@ -182,7 +203,7 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
|
||||
# Monthly hit chart
|
||||
add_hitchart(
|
||||
'monthly',
|
||||
"monthly",
|
||||
Time.now() - Time.SECONDS_IN_DAY * 30,
|
||||
Time.now(),
|
||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 30),
|
||||
@ -190,7 +211,7 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
|
||||
# All time hit chart
|
||||
add_hitchart(
|
||||
'total',
|
||||
"total",
|
||||
Time.now() - Time.SECONDS_IN_DAY * 365,
|
||||
Time.now(),
|
||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 365),
|
||||
@ -199,13 +220,13 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
return root
|
||||
|
||||
def handle_info_rb5_info_read_shop_ranking_request(self, request: Node) -> Node:
|
||||
start_music_id = request.child_value('min')
|
||||
end_music_id = request.child_value('max')
|
||||
start_music_id = request.child_value("min")
|
||||
end_music_id = request.child_value("max")
|
||||
|
||||
root = Node.void('info')
|
||||
shop_score = Node.void('shop_score')
|
||||
root = Node.void("info")
|
||||
shop_score = Node.void("shop_score")
|
||||
root.add_child(shop_score)
|
||||
shop_score.add_child(Node.s32('time', Time.now()))
|
||||
shop_score.add_child(Node.s32("time", Time.now()))
|
||||
|
||||
profiles: Dict[UserID, Profile] = {}
|
||||
for songid in range(start_music_id, end_music_id + 1):
|
||||
@ -233,31 +254,44 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
profiles[userid] = self.get_any_profile(userid)
|
||||
profile = profiles[userid]
|
||||
|
||||
data = Node.void('data')
|
||||
data = Node.void("data")
|
||||
shop_score.add_child(data)
|
||||
data.add_child(Node.s32('rank', i + 1))
|
||||
data.add_child(Node.s16('music_id', songid))
|
||||
data.add_child(Node.s8('note_grade', score.chart))
|
||||
data.add_child(Node.s8('clear_type', self._db_to_game_clear_type(score.data.get_int('clear_type'))))
|
||||
data.add_child(Node.s32('user_id', profile.extid))
|
||||
data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id')))
|
||||
data.add_child(Node.s32('score', score.points))
|
||||
data.add_child(Node.s32('time', score.timestamp))
|
||||
data.add_child(Node.string('name', profile.get_str('name')))
|
||||
data.add_child(Node.s32("rank", i + 1))
|
||||
data.add_child(Node.s16("music_id", songid))
|
||||
data.add_child(Node.s8("note_grade", score.chart))
|
||||
data.add_child(
|
||||
Node.s8(
|
||||
"clear_type",
|
||||
self._db_to_game_clear_type(
|
||||
score.data.get_int("clear_type")
|
||||
),
|
||||
)
|
||||
)
|
||||
data.add_child(Node.s32("user_id", profile.extid))
|
||||
data.add_child(
|
||||
Node.s16(
|
||||
"icon_id", profile.get_dict("config").get_int("icon_id")
|
||||
)
|
||||
)
|
||||
data.add_child(Node.s32("score", score.points))
|
||||
data.add_child(Node.s32("time", score.timestamp))
|
||||
data.add_child(Node.string("name", profile.get_str("name")))
|
||||
|
||||
return root
|
||||
|
||||
def handle_lobby_rb5_lobby_entry_request(self, request: Node) -> Node:
|
||||
root = Node.void('lobby')
|
||||
root.add_child(Node.s32('interval', 120))
|
||||
root.add_child(Node.s32('interval_p', 120))
|
||||
root = Node.void("lobby")
|
||||
root.add_child(Node.s32("interval", 120))
|
||||
root.add_child(Node.s32("interval_p", 120))
|
||||
|
||||
# Create a lobby entry for this user
|
||||
extid = request.child_value('e/uid')
|
||||
extid = request.child_value("e/uid")
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid)
|
||||
info = self.data.local.lobby.get_play_session_info(self.game, self.version, userid)
|
||||
info = self.data.local.lobby.get_play_session_info(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
if profile is None or info is None:
|
||||
return root
|
||||
|
||||
@ -266,61 +300,61 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
self.version,
|
||||
userid,
|
||||
{
|
||||
'mid': request.child_value('e/mid'),
|
||||
'ng': request.child_value('e/ng'),
|
||||
'mopt': request.child_value('e/mopt'),
|
||||
'lid': request.child_value('e/lid'),
|
||||
'sn': request.child_value('e/sn'),
|
||||
'pref': request.child_value('e/pref'),
|
||||
'stg': request.child_value('e/stg'),
|
||||
'pside': request.child_value('e/pside'),
|
||||
'eatime': request.child_value('e/eatime'),
|
||||
'ga': request.child_value('e/ga'),
|
||||
'gp': request.child_value('e/gp'),
|
||||
'la': request.child_value('e/la'),
|
||||
'ver': request.child_value('e/ver'),
|
||||
}
|
||||
"mid": request.child_value("e/mid"),
|
||||
"ng": request.child_value("e/ng"),
|
||||
"mopt": request.child_value("e/mopt"),
|
||||
"lid": request.child_value("e/lid"),
|
||||
"sn": request.child_value("e/sn"),
|
||||
"pref": request.child_value("e/pref"),
|
||||
"stg": request.child_value("e/stg"),
|
||||
"pside": request.child_value("e/pside"),
|
||||
"eatime": request.child_value("e/eatime"),
|
||||
"ga": request.child_value("e/ga"),
|
||||
"gp": request.child_value("e/gp"),
|
||||
"la": request.child_value("e/la"),
|
||||
"ver": request.child_value("e/ver"),
|
||||
},
|
||||
)
|
||||
lobby = self.data.local.lobby.get_lobby(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
)
|
||||
root.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e = Node.void('e')
|
||||
root.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e = Node.void("e")
|
||||
root.add_child(e)
|
||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
||||
e.add_child(Node.s32('uid', profile.extid))
|
||||
e.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
||||
e.add_child(Node.s32('plyid', info.get_int('id')))
|
||||
e.add_child(Node.s16('mg', profile.get_int('mg')))
|
||||
e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
|
||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
||||
e.add_child(Node.u8('pref', lobby.get_int('pref')))
|
||||
e.add_child(Node.s8('stg', lobby.get_int('stg')))
|
||||
e.add_child(Node.s8('pside', lobby.get_int('pside')))
|
||||
e.add_child(Node.s16('eatime', lobby.get_int('eatime')))
|
||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
||||
e.add_child(Node.u8('ver', lobby.get_int('ver')))
|
||||
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||
e.add_child(Node.s32("uid", profile.extid))
|
||||
e.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||
e.add_child(Node.s32("plyid", info.get_int("id")))
|
||||
e.add_child(Node.s16("mg", profile.get_int("mg")))
|
||||
e.add_child(Node.s32("mopt", lobby.get_int("mopt")))
|
||||
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||
e.add_child(Node.string("sn", lobby.get_str("sn")))
|
||||
e.add_child(Node.u8("pref", lobby.get_int("pref")))
|
||||
e.add_child(Node.s8("stg", lobby.get_int("stg")))
|
||||
e.add_child(Node.s8("pside", lobby.get_int("pside")))
|
||||
e.add_child(Node.s16("eatime", lobby.get_int("eatime")))
|
||||
e.add_child(Node.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||
e.add_child(Node.u16("gp", lobby.get_int("gp")))
|
||||
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||
e.add_child(Node.u8("ver", lobby.get_int("ver")))
|
||||
|
||||
return root
|
||||
|
||||
def handle_lobby_rb5_lobby_read_request(self, request: Node) -> Node:
|
||||
root = Node.void('lobby')
|
||||
root.add_child(Node.s32('interval', 120))
|
||||
root.add_child(Node.s32('interval_p', 120))
|
||||
root = Node.void("lobby")
|
||||
root.add_child(Node.s32("interval", 120))
|
||||
root.add_child(Node.s32("interval_p", 120))
|
||||
|
||||
# Look up all lobbies matching the criteria specified
|
||||
ver = request.child_value('var')
|
||||
mg = request.child_value('m_grade') # noqa: F841
|
||||
extid = request.child_value('uid')
|
||||
limit = request.child_value('max')
|
||||
ver = request.child_value("var")
|
||||
mg = request.child_value("m_grade") # noqa: F841
|
||||
extid = request.child_value("uid")
|
||||
limit = request.child_value("max")
|
||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||
if userid is not None:
|
||||
lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version)
|
||||
@ -331,95 +365,99 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
if user == userid:
|
||||
# If we have our own lobby, don't return it
|
||||
continue
|
||||
if ver != lobby.get_int('ver'):
|
||||
if ver != lobby.get_int("ver"):
|
||||
# Don't return lobby data for different versions
|
||||
continue
|
||||
|
||||
profile = self.get_profile(user)
|
||||
info = self.data.local.lobby.get_play_session_info(self.game, self.version, userid)
|
||||
info = self.data.local.lobby.get_play_session_info(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
if profile is None or info is None:
|
||||
# No profile info, don't return this lobby
|
||||
return root
|
||||
|
||||
e = Node.void('e')
|
||||
e = Node.void("e")
|
||||
root.add_child(e)
|
||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
||||
e.add_child(Node.s32('uid', profile.extid))
|
||||
e.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
||||
e.add_child(Node.s32('plyid', info.get_int('id')))
|
||||
e.add_child(Node.s16('mg', profile.get_int('mg')))
|
||||
e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
|
||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
||||
e.add_child(Node.u8('pref', lobby.get_int('pref')))
|
||||
e.add_child(Node.s8('stg', lobby.get_int('stg')))
|
||||
e.add_child(Node.s8('pside', lobby.get_int('pside')))
|
||||
e.add_child(Node.s16('eatime', lobby.get_int('eatime')))
|
||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
||||
e.add_child(Node.u8('ver', lobby.get_int('ver')))
|
||||
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||
e.add_child(Node.s32("uid", profile.extid))
|
||||
e.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||
e.add_child(Node.s32("plyid", info.get_int("id")))
|
||||
e.add_child(Node.s16("mg", profile.get_int("mg")))
|
||||
e.add_child(Node.s32("mopt", lobby.get_int("mopt")))
|
||||
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||
e.add_child(Node.string("sn", lobby.get_str("sn")))
|
||||
e.add_child(Node.u8("pref", lobby.get_int("pref")))
|
||||
e.add_child(Node.s8("stg", lobby.get_int("stg")))
|
||||
e.add_child(Node.s8("pside", lobby.get_int("pside")))
|
||||
e.add_child(Node.s16("eatime", lobby.get_int("eatime")))
|
||||
e.add_child(Node.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||
e.add_child(Node.u16("gp", lobby.get_int("gp")))
|
||||
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||
e.add_child(Node.u8("ver", lobby.get_int("ver")))
|
||||
|
||||
limit = limit - 1
|
||||
|
||||
return root
|
||||
|
||||
def handle_lobby_rb5_lobby_delete_entry_request(self, request: Node) -> Node:
|
||||
eid = request.child_value('eid')
|
||||
eid = request.child_value("eid")
|
||||
self.data.local.lobby.destroy_lobby(eid)
|
||||
return Node.void('lobby')
|
||||
return Node.void("lobby")
|
||||
|
||||
def handle_pcb_rb5_pcb_boot_request(self, request: Node) -> Node:
|
||||
shop_id = ID.parse_machine_id(request.child_value('lid'))
|
||||
shop_id = ID.parse_machine_id(request.child_value("lid"))
|
||||
machine = self.get_machine_by_id(shop_id)
|
||||
if machine is not None:
|
||||
machine_name = machine.name
|
||||
close = machine.data.get_bool('close')
|
||||
hour = machine.data.get_int('hour')
|
||||
minute = machine.data.get_int('minute')
|
||||
close = machine.data.get_bool("close")
|
||||
hour = machine.data.get_int("hour")
|
||||
minute = machine.data.get_int("minute")
|
||||
else:
|
||||
machine_name = ''
|
||||
machine_name = ""
|
||||
close = False
|
||||
hour = 0
|
||||
minute = 0
|
||||
|
||||
root = Node.void('pcb')
|
||||
sinfo = Node.void('sinfo')
|
||||
root = Node.void("pcb")
|
||||
sinfo = Node.void("sinfo")
|
||||
root.add_child(sinfo)
|
||||
sinfo.add_child(Node.string('nm', machine_name))
|
||||
sinfo.add_child(Node.bool('cl_enbl', close))
|
||||
sinfo.add_child(Node.u8('cl_h', hour))
|
||||
sinfo.add_child(Node.u8('cl_m', minute))
|
||||
sinfo.add_child(Node.bool('shop_flag', True))
|
||||
sinfo.add_child(Node.string("nm", machine_name))
|
||||
sinfo.add_child(Node.bool("cl_enbl", close))
|
||||
sinfo.add_child(Node.u8("cl_h", hour))
|
||||
sinfo.add_child(Node.u8("cl_m", minute))
|
||||
sinfo.add_child(Node.bool("shop_flag", True))
|
||||
return root
|
||||
|
||||
def handle_pcb_rb5_pcb_error_request(self, request: Node) -> Node:
|
||||
return Node.void('pcb')
|
||||
return Node.void("pcb")
|
||||
|
||||
def handle_pcb_rb5_pcb_update_request(self, request: Node) -> Node:
|
||||
return Node.void('pcb')
|
||||
return Node.void("pcb")
|
||||
|
||||
def handle_shop_rb5_shop_write_setting_request(self, request: Node) -> Node:
|
||||
return Node.void('shop')
|
||||
return Node.void("shop")
|
||||
|
||||
def handle_shop_rb5_shop_write_info_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.child_value('sinfo/nm'))
|
||||
self.update_machine_data({
|
||||
'close': request.child_value('sinfo/cl_enbl'),
|
||||
'hour': request.child_value('sinfo/cl_h'),
|
||||
'minute': request.child_value('sinfo/cl_m'),
|
||||
'pref': request.child_value('sinfo/prf'),
|
||||
})
|
||||
return Node.void('shop')
|
||||
self.update_machine_name(request.child_value("sinfo/nm"))
|
||||
self.update_machine_data(
|
||||
{
|
||||
"close": request.child_value("sinfo/cl_enbl"),
|
||||
"hour": request.child_value("sinfo/cl_h"),
|
||||
"minute": request.child_value("sinfo/cl_m"),
|
||||
"pref": request.child_value("sinfo/prf"),
|
||||
}
|
||||
)
|
||||
return Node.void("shop")
|
||||
|
||||
def handle_player_rb5_player_start_request(self, request: Node) -> Node:
|
||||
root = Node.void('player')
|
||||
root = Node.void("player")
|
||||
|
||||
# Create a new play session based on info from the request
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
self.data.local.lobby.put_play_session_info(
|
||||
@ -427,10 +465,10 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
self.version,
|
||||
userid,
|
||||
{
|
||||
'ga': request.child_value('ga'),
|
||||
'gp': request.child_value('gp'),
|
||||
'la': request.child_value('la'),
|
||||
'pnid': request.child_value('pnid'),
|
||||
"ga": request.child_value("ga"),
|
||||
"gp": request.child_value("gp"),
|
||||
"la": request.child_value("la"),
|
||||
"pnid": request.child_value("pnid"),
|
||||
},
|
||||
)
|
||||
info = self.data.local.lobby.get_play_session_info(
|
||||
@ -439,22 +477,22 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
userid,
|
||||
)
|
||||
if info is not None:
|
||||
play_id = info.get_int('id')
|
||||
play_id = info.get_int("id")
|
||||
else:
|
||||
play_id = 0
|
||||
else:
|
||||
play_id = 0
|
||||
|
||||
# Session stuff, and resend global defaults
|
||||
root.add_child(Node.s32('plyid', play_id))
|
||||
root.add_child(Node.u64('start_time', Time.now() * 1000))
|
||||
root.add_child(Node.s32("plyid", play_id))
|
||||
root.add_child(Node.u64("start_time", Time.now() * 1000))
|
||||
self._add_event_info(root)
|
||||
|
||||
return root
|
||||
|
||||
def handle_player_rb5_player_end_request(self, request: Node) -> Node:
|
||||
# Destroy play session based on info from the request
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
# Kill any lingering lobbies by this user
|
||||
@ -464,16 +502,18 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
userid,
|
||||
)
|
||||
if lobby is not None:
|
||||
self.data.local.lobby.destroy_lobby(lobby.get_int('id'))
|
||||
self.data.local.lobby.destroy_play_session_info(self.game, self.version, userid)
|
||||
self.data.local.lobby.destroy_lobby(lobby.get_int("id"))
|
||||
self.data.local.lobby.destroy_play_session_info(
|
||||
self.game, self.version, userid
|
||||
)
|
||||
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
||||
def handle_player_rb5_player_delete_request(self, request: Node) -> Node:
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
||||
def handle_player_rb5_player_succeed_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
previous_version = self.previous_version()
|
||||
@ -481,25 +521,25 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
||||
else:
|
||||
profile = None
|
||||
|
||||
root = Node.void('player')
|
||||
root = Node.void("player")
|
||||
|
||||
if profile is None:
|
||||
# Return empty succeed to say this is new
|
||||
root.add_child(Node.string('name', ''))
|
||||
root.add_child(Node.s32('grd', -1))
|
||||
root.add_child(Node.s32('ap', -1))
|
||||
root.add_child(Node.s32('uattr', 0))
|
||||
root.add_child(Node.string("name", ""))
|
||||
root.add_child(Node.s32("grd", -1))
|
||||
root.add_child(Node.s32("ap", -1))
|
||||
root.add_child(Node.s32("uattr", 0))
|
||||
else:
|
||||
# Return previous profile formatted to say this is data succession
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root.add_child(Node.s32('grd', profile.get_int('mg'))) # This is a guess
|
||||
root.add_child(Node.s32('ap', profile.get_int('ap')))
|
||||
root.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
root.add_child(Node.s32("grd", profile.get_int("mg"))) # This is a guess
|
||||
root.add_child(Node.s32("ap", profile.get_int("ap")))
|
||||
root.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||
return root
|
||||
|
||||
def handle_player_rb5_player_read_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('rid')
|
||||
refid = request.child_value("rid")
|
||||
profile = self.get_profile_by_refid(refid)
|
||||
if profile:
|
||||
return profile
|
||||
return Node.void('player')
|
||||
return Node.void("player")
|
||||
|
@ -21,7 +21,9 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
CLEAR_TYPE_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_CLEAR
|
||||
CLEAR_TYPE_HARD_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR
|
||||
CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN
|
||||
CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN
|
||||
CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[
|
||||
int
|
||||
] = DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN
|
||||
|
||||
GRADE_NO_PLAY: Final[int] = DBConstants.SDVX_GRADE_NO_PLAY
|
||||
GRADE_D: Final[int] = DBConstants.SDVX_GRADE_D
|
||||
@ -41,7 +43,7 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
CHART_TYPE_INFINITE: Final[int] = 3
|
||||
CHART_TYPE_MAXIMUM: Final[int] = 4
|
||||
|
||||
def previous_version(self) -> Optional['SoundVoltexBase']:
|
||||
def previous_version(self) -> Optional["SoundVoltexBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
@ -66,7 +68,9 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
# Now, return it
|
||||
return self.format_profile(userid, profile)
|
||||
|
||||
def new_profile_by_refid(self, refid: Optional[str], name: Optional[str], locid: Optional[int]) -> Node:
|
||||
def new_profile_by_refid(
|
||||
self, refid: Optional[str], name: Optional[str], locid: Optional[int]
|
||||
) -> Node:
|
||||
"""
|
||||
Given a RefID and an optional name, create a profile and then return
|
||||
a formatted profile node. Similar rationale to get_profile_by_refid.
|
||||
@ -75,7 +79,7 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
return None
|
||||
|
||||
if name is None:
|
||||
name = 'NONAME'
|
||||
name = "NONAME"
|
||||
|
||||
# First, create and save the default profile
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -85,8 +89,8 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
refid,
|
||||
0,
|
||||
{
|
||||
'name': name,
|
||||
'loc': locid,
|
||||
"name": name,
|
||||
"loc": locid,
|
||||
},
|
||||
)
|
||||
self.put_profile(userid, profile)
|
||||
@ -97,9 +101,11 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
Base handler for a profile. Given a userid and a profile dictionary,
|
||||
return a Node representing a profile. Should be overridden.
|
||||
"""
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
"""
|
||||
Base handler for profile parsing. Given a request and an old profile,
|
||||
return a new profile that's been updated with the contents of the request.
|
||||
@ -121,16 +127,18 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
},
|
||||
}
|
||||
"""
|
||||
all_attempts, remote_attempts = Parallel.execute([
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.version,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.version,
|
||||
)
|
||||
])
|
||||
all_attempts, remote_attempts = Parallel.execute(
|
||||
[
|
||||
lambda: self.data.local.music.get_all_attempts(
|
||||
game=self.game,
|
||||
version=self.version,
|
||||
),
|
||||
lambda: self.data.remote.music.get_clear_rates(
|
||||
game=self.game,
|
||||
version=self.version,
|
||||
),
|
||||
]
|
||||
)
|
||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||
for (_, attempt) in all_attempts:
|
||||
# Terrible temporary structure is terrible.
|
||||
@ -138,26 +146,33 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
attempts[attempt.id] = {}
|
||||
if attempt.chart not in attempts[attempt.id]:
|
||||
attempts[attempt.id][attempt.chart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
'average': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
"average": 0,
|
||||
}
|
||||
|
||||
# We saw an attempt, keep the total attempts in sync.
|
||||
attempts[attempt.id][attempt.chart]['average'] = int(
|
||||
attempts[attempt.id][attempt.chart]["average"] = int(
|
||||
(
|
||||
(attempts[attempt.id][attempt.chart]['average'] * attempts[attempt.id][attempt.chart]['total']) +
|
||||
attempt.points
|
||||
) / (attempts[attempt.id][attempt.chart]['total'] + 1)
|
||||
(
|
||||
attempts[attempt.id][attempt.chart]["average"]
|
||||
* attempts[attempt.id][attempt.chart]["total"]
|
||||
)
|
||||
+ attempt.points
|
||||
)
|
||||
/ (attempts[attempt.id][attempt.chart]["total"] + 1)
|
||||
)
|
||||
attempts[attempt.id][attempt.chart]['total'] += 1
|
||||
attempts[attempt.id][attempt.chart]["total"] += 1
|
||||
|
||||
if attempt.data.get_int('clear_type', self.CLEAR_TYPE_NO_PLAY) in [self.CLEAR_TYPE_NO_PLAY, self.CLEAR_TYPE_FAILED]:
|
||||
if attempt.data.get_int("clear_type", self.CLEAR_TYPE_NO_PLAY) in [
|
||||
self.CLEAR_TYPE_NO_PLAY,
|
||||
self.CLEAR_TYPE_FAILED,
|
||||
]:
|
||||
# This attempt was a failure, so don't count it against clears of full combos
|
||||
continue
|
||||
|
||||
# It was at least a clear
|
||||
attempts[attempt.id][attempt.chart]['clears'] += 1
|
||||
attempts[attempt.id][attempt.chart]["clears"] += 1
|
||||
|
||||
# Merge in remote attempts
|
||||
for songid in remote_attempts:
|
||||
@ -167,13 +182,17 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
for songchart in remote_attempts[songid]:
|
||||
if songchart not in attempts[songid]:
|
||||
attempts[songid][songchart] = {
|
||||
'total': 0,
|
||||
'clears': 0,
|
||||
'average': 0,
|
||||
"total": 0,
|
||||
"clears": 0,
|
||||
"average": 0,
|
||||
}
|
||||
|
||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
||||
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["plays"]
|
||||
attempts[songid][songchart]["clears"] += remote_attempts[songid][
|
||||
songchart
|
||||
]["clears"]
|
||||
|
||||
return attempts
|
||||
|
||||
@ -186,7 +205,7 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
clear_type: int,
|
||||
grade: int,
|
||||
combo: int,
|
||||
stats: Optional[Dict[str, int]]=None,
|
||||
stats: Optional[Dict[str, int]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Given various pieces of a score, update the user's high score and score
|
||||
@ -248,21 +267,23 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
scoredata = oldscore.data
|
||||
|
||||
# Replace clear type and grade
|
||||
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
|
||||
history.replace_int('clear_type', clear_type)
|
||||
scoredata.replace_int('grade', max(scoredata.get_int('grade'), grade))
|
||||
history.replace_int('grade', grade)
|
||||
scoredata.replace_int(
|
||||
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||
)
|
||||
history.replace_int("clear_type", clear_type)
|
||||
scoredata.replace_int("grade", max(scoredata.get_int("grade"), grade))
|
||||
history.replace_int("grade", grade)
|
||||
|
||||
# If we have a combo, replace it
|
||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
||||
history.replace_int('combo', combo)
|
||||
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||
history.replace_int("combo", combo)
|
||||
|
||||
# If we have play stats, replace it
|
||||
if stats is not None:
|
||||
if raised:
|
||||
# We have stats, and there's a new high score, update the stats
|
||||
scoredata.replace_dict('stats', stats)
|
||||
history.replace_dict('stats', stats)
|
||||
scoredata.replace_dict("stats", stats)
|
||||
history.replace_dict("stats", stats)
|
||||
|
||||
# Look up where this score was earned
|
||||
lid = self.get_machine_id()
|
||||
|
@ -14,7 +14,7 @@ class SoundVoltexBooth(
|
||||
SoundVoltexBase,
|
||||
):
|
||||
|
||||
name: str = 'SOUND VOLTEX BOOTH'
|
||||
name: str = "SOUND VOLTEX BOOTH"
|
||||
version: int = VersionConstants.SDVX_BOOTH
|
||||
|
||||
GAME_LIMITED_LOCKED: Final[int] = 1
|
||||
@ -42,24 +42,24 @@ class SoundVoltexBooth(
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Disable Online Matching',
|
||||
'tip': 'Disable online matching between games.',
|
||||
'category': 'game_config',
|
||||
'setting': 'disable_matching',
|
||||
"name": "Disable Online Matching",
|
||||
"tip": "Disable online matching between games.",
|
||||
"category": "game_config",
|
||||
"setting": "disable_matching",
|
||||
},
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
{
|
||||
'name': 'Force Appeal Card Unlock',
|
||||
'tip': 'Force unlock all appeal cards.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_cards',
|
||||
"name": "Force Appeal Card Unlock",
|
||||
"tip": "Force unlock all appeal cards.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_cards",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -112,108 +112,108 @@ class SoundVoltexBooth(
|
||||
}[grade]
|
||||
|
||||
def handle_game_exception_request(self, request: Node) -> Node:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_entry_s_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
# This should be created on the fly for a lobby that we're in.
|
||||
game.add_child(Node.u32('entry_id', 1))
|
||||
game.add_child(Node.u32("entry_id", 1))
|
||||
return game
|
||||
|
||||
def handle_game_lounge_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
# Refresh interval in seconds.
|
||||
game.add_child(Node.u32('interval', 10))
|
||||
game.add_child(Node.u32("interval", 10))
|
||||
return game
|
||||
|
||||
def handle_game_entry_e_request(self, request: Node) -> Node:
|
||||
# Lobby destroy method, eid attribute (u32) should be used
|
||||
# to destroy any open lobbies.
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_frozen_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game.set_attribute('result', '0')
|
||||
game = Node.void("game")
|
||||
game.set_attribute("result", "0")
|
||||
return game
|
||||
|
||||
def handle_game_shop_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.child_value('shopname'))
|
||||
self.update_machine_name(request.child_value("shopname"))
|
||||
|
||||
# Respond with number of milliseconds until next request
|
||||
game = Node.void('game')
|
||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
||||
game = Node.void("game")
|
||||
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||
return game
|
||||
|
||||
def handle_game_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
limited = Node.void('limited')
|
||||
game = Node.void("game")
|
||||
limited = Node.void("limited")
|
||||
game.add_child(limited)
|
||||
|
||||
game_config = self.get_game_config()
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids = set()
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
||||
for song in songs:
|
||||
if song.data.get_int('limited') == self.GAME_LIMITED_LOCKED:
|
||||
if song.data.get_int("limited") == self.GAME_LIMITED_LOCKED:
|
||||
ids.add(song.id)
|
||||
|
||||
for songid in ids:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
limited.add_child(music)
|
||||
music.set_attribute('id', str(songid))
|
||||
music.set_attribute('flag', str(self.GAME_LIMITED_UNLOCKED))
|
||||
music.set_attribute("id", str(songid))
|
||||
music.set_attribute("flag", str(self.GAME_LIMITED_UNLOCKED))
|
||||
|
||||
event = Node.void('event')
|
||||
event = Node.void("event")
|
||||
game.add_child(event)
|
||||
|
||||
def enable_event(eid: int) -> None:
|
||||
evt = Node.void('info')
|
||||
evt = Node.void("info")
|
||||
event.add_child(evt)
|
||||
evt.set_attribute('id', str(eid))
|
||||
evt.set_attribute("id", str(eid))
|
||||
|
||||
if not game_config.get_bool('disable_matching'):
|
||||
if not game_config.get_bool("disable_matching"):
|
||||
enable_event(3) # Matching enabled
|
||||
enable_event(9) # Rank Soukuu
|
||||
enable_event(13) # Year-end bonus
|
||||
|
||||
catalog = Node.void('catalog')
|
||||
catalog = Node.void("catalog")
|
||||
game.add_child(catalog)
|
||||
songunlocks = self.data.local.game.get_items(self.game, self.version)
|
||||
for unlock in songunlocks:
|
||||
if unlock.type != 'song_unlock':
|
||||
if unlock.type != "song_unlock":
|
||||
continue
|
||||
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
catalog.add_child(info)
|
||||
info.set_attribute('id', str(unlock.id))
|
||||
info.set_attribute('currency', str(self.GAME_CURRENCY_BLOCKS))
|
||||
info.set_attribute('price', str(unlock.data.get_int('blocks')))
|
||||
info.set_attribute("id", str(unlock.id))
|
||||
info.set_attribute("currency", str(self.GAME_CURRENCY_BLOCKS))
|
||||
info.set_attribute("price", str(unlock.data.get_int("blocks")))
|
||||
|
||||
kacinfo = Node.void('kacinfo')
|
||||
kacinfo = Node.void("kacinfo")
|
||||
game.add_child(kacinfo)
|
||||
kacinfo.add_child(Node.u32('note00', 0))
|
||||
kacinfo.add_child(Node.u32('note01', 0))
|
||||
kacinfo.add_child(Node.u32('note02', 0))
|
||||
kacinfo.add_child(Node.u32('note10', 0))
|
||||
kacinfo.add_child(Node.u32('note11', 0))
|
||||
kacinfo.add_child(Node.u32('note12', 0))
|
||||
kacinfo.add_child(Node.u32('rabbeat0', 0))
|
||||
kacinfo.add_child(Node.u32('rabbeat1', 0))
|
||||
kacinfo.add_child(Node.u32("note00", 0))
|
||||
kacinfo.add_child(Node.u32("note01", 0))
|
||||
kacinfo.add_child(Node.u32("note02", 0))
|
||||
kacinfo.add_child(Node.u32("note10", 0))
|
||||
kacinfo.add_child(Node.u32("note11", 0))
|
||||
kacinfo.add_child(Node.u32("note12", 0))
|
||||
kacinfo.add_child(Node.u32("rabbeat0", 0))
|
||||
kacinfo.add_child(Node.u32("rabbeat1", 0))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_hiscore_request(self, request: Node) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
|
||||
# Ranking system I think?
|
||||
for i in range(1, 21):
|
||||
ranking = Node.void('ranking')
|
||||
ranking = Node.void("ranking")
|
||||
game.add_child(ranking)
|
||||
ranking.set_attribute('id', str(i))
|
||||
ranking.set_attribute("id", str(i))
|
||||
|
||||
hiscore = Node.void('hiscore')
|
||||
hiscore = Node.void("hiscore")
|
||||
game.add_child(hiscore)
|
||||
hiscore.set_attribute('type', '1')
|
||||
hiscore.set_attribute("type", "1")
|
||||
|
||||
records = self.data.remote.music.get_all_records(self.game, self.version)
|
||||
|
||||
@ -228,44 +228,47 @@ class SoundVoltexBooth(
|
||||
records_by_id[score.id][score.chart] = record
|
||||
missing_users.append(userid)
|
||||
|
||||
users = {userid: profile for (userid, profile) in self.get_any_profiles(missing_users)}
|
||||
users = {
|
||||
userid: profile
|
||||
for (userid, profile) in self.get_any_profiles(missing_users)
|
||||
}
|
||||
|
||||
# Output records
|
||||
for songid in records_by_id:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
hiscore.add_child(music)
|
||||
music.set_attribute('id', str(songid))
|
||||
music.set_attribute("id", str(songid))
|
||||
|
||||
for chart in records_by_id[songid]:
|
||||
note = Node.void('note')
|
||||
note = Node.void("note")
|
||||
music.add_child(note)
|
||||
note.set_attribute('type', str(chart))
|
||||
note.set_attribute("type", str(chart))
|
||||
|
||||
userid, score = records_by_id[songid][chart]
|
||||
note.set_attribute('score', str(score.points))
|
||||
note.set_attribute('name', users[userid].get_str('name'))
|
||||
note.set_attribute("score", str(score.points))
|
||||
note.set_attribute("name", users[userid].get_str("name"))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_new_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
name = request.attribute('name')
|
||||
loc = ID.parse_machine_id(request.attribute('locid'))
|
||||
refid = request.attribute("refid")
|
||||
name = request.attribute("name")
|
||||
loc = ID.parse_machine_id(request.attribute("locid"))
|
||||
self.new_profile_by_refid(refid, name, loc)
|
||||
|
||||
root = Node.void('game')
|
||||
root = Node.void("game")
|
||||
return root
|
||||
|
||||
def handle_game_load_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('dataid')
|
||||
refid = request.attribute("dataid")
|
||||
root = self.get_profile_by_refid(refid)
|
||||
if root is None:
|
||||
root = Node.void('game')
|
||||
root.set_attribute('none', '1')
|
||||
root = Node.void("game")
|
||||
root.set_attribute("none", "1")
|
||||
return root
|
||||
|
||||
def handle_game_save_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -281,10 +284,10 @@ class SoundVoltexBooth(
|
||||
if userid is not None and newprofile is not None:
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('dataid')
|
||||
refid = request.attribute("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -305,27 +308,33 @@ class SoundVoltexBooth(
|
||||
scores_by_id[score.id][score.chart] = score
|
||||
|
||||
# Output to the game
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
for songid in scores_by_id:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
game.add_child(music)
|
||||
music.set_attribute('music_id', str(songid))
|
||||
music.set_attribute("music_id", str(songid))
|
||||
|
||||
for chart in scores_by_id[songid]:
|
||||
typenode = Node.void('type')
|
||||
typenode = Node.void("type")
|
||||
music.add_child(typenode)
|
||||
typenode.set_attribute('type_id', str(chart))
|
||||
typenode.set_attribute("type_id", str(chart))
|
||||
|
||||
score = scores_by_id[songid][chart]
|
||||
typenode.set_attribute('score', str(score.points))
|
||||
typenode.set_attribute('cnt', str(score.plays))
|
||||
typenode.set_attribute('clear_type', str(self.__db_to_game_clear_type(score.data.get_int('clear_type'))))
|
||||
typenode.set_attribute('score_grade', str(self.__db_to_game_grade(score.data.get_int('grade'))))
|
||||
typenode.set_attribute("score", str(score.points))
|
||||
typenode.set_attribute("cnt", str(score.plays))
|
||||
typenode.set_attribute(
|
||||
"clear_type",
|
||||
str(self.__db_to_game_clear_type(score.data.get_int("clear_type"))),
|
||||
)
|
||||
typenode.set_attribute(
|
||||
"score_grade",
|
||||
str(self.__db_to_game_grade(score.data.get_int("grade"))),
|
||||
)
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('dataid')
|
||||
refid = request.attribute("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -333,14 +342,14 @@ class SoundVoltexBooth(
|
||||
userid = None
|
||||
|
||||
if userid is None:
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
musicid = int(request.attribute('music_id'))
|
||||
chart = int(request.attribute('music_type'))
|
||||
score = int(request.attribute('score'))
|
||||
combo = int(request.attribute('max_chain'))
|
||||
grade = self.__game_to_db_grade(int(request.attribute('score_grade')))
|
||||
clear_type = self.__game_to_db_clear_type(int(request.attribute('clear_type')))
|
||||
musicid = int(request.attribute("music_id"))
|
||||
chart = int(request.attribute("music_type"))
|
||||
score = int(request.attribute("score"))
|
||||
combo = int(request.attribute("max_chain"))
|
||||
grade = self.__game_to_db_grade(int(request.attribute("score_grade")))
|
||||
clear_type = self.__game_to_db_clear_type(int(request.attribute("clear_type")))
|
||||
|
||||
# Save the score
|
||||
self.update_score(
|
||||
@ -354,10 +363,10 @@ class SoundVoltexBooth(
|
||||
)
|
||||
|
||||
# No response necessary
|
||||
return Node.void('game')
|
||||
return Node.void("game")
|
||||
|
||||
def handle_game_buy_request(self, request: Node) -> Node:
|
||||
refid = request.attribute('refid')
|
||||
refid = request.attribute("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -371,21 +380,26 @@ class SoundVoltexBooth(
|
||||
|
||||
if userid is not None and profile is not None:
|
||||
# Look up packets and blocks
|
||||
packet = profile.get_int('packet')
|
||||
block = profile.get_int('block')
|
||||
packet = profile.get_int("packet")
|
||||
block = profile.get_int("block")
|
||||
|
||||
# Add on any additional we earned this round
|
||||
packet = packet + (request.child_value('earned_gamecoin_packet') or 0)
|
||||
block = block + (request.child_value('earned_gamecoin_block') or 0)
|
||||
packet = packet + (request.child_value("earned_gamecoin_packet") or 0)
|
||||
block = block + (request.child_value("earned_gamecoin_block") or 0)
|
||||
|
||||
# Look up the item to get the actual price and currency used
|
||||
item = self.data.local.game.get_item(self.game, self.version, request.child_value('catalog_id'), 'song_unlock')
|
||||
item = self.data.local.game.get_item(
|
||||
self.game,
|
||||
self.version,
|
||||
request.child_value("catalog_id"),
|
||||
"song_unlock",
|
||||
)
|
||||
if item is not None:
|
||||
currency_type = request.child_value('currency_type')
|
||||
currency_type = request.child_value("currency_type")
|
||||
if currency_type == self.GAME_CURRENCY_PACKETS:
|
||||
if 'packets' in item:
|
||||
if "packets" in item:
|
||||
# This is a valid purchase
|
||||
newpacket = packet - item.get_int('packets')
|
||||
newpacket = packet - item.get_int("packets")
|
||||
if newpacket < 0:
|
||||
result = 1
|
||||
else:
|
||||
@ -395,9 +409,9 @@ class SoundVoltexBooth(
|
||||
# Bad transaction
|
||||
result = 1
|
||||
elif currency_type == self.GAME_CURRENCY_BLOCKS:
|
||||
if 'blocks' in item:
|
||||
if "blocks" in item:
|
||||
# This is a valid purchase
|
||||
newblock = block - item.get_int('blocks')
|
||||
newblock = block - item.get_int("blocks")
|
||||
if newblock < 0:
|
||||
result = 1
|
||||
else:
|
||||
@ -412,8 +426,8 @@ class SoundVoltexBooth(
|
||||
|
||||
if result == 0:
|
||||
# Transaction is valid, update the profile with new packets and blocks
|
||||
profile.replace_int('packet', packet)
|
||||
profile.replace_int('block', block)
|
||||
profile.replace_int("packet", packet)
|
||||
profile.replace_int("block", block)
|
||||
self.put_profile(userid, profile)
|
||||
else:
|
||||
# Bad catalog ID
|
||||
@ -424,97 +438,117 @@ class SoundVoltexBooth(
|
||||
block = 0
|
||||
result = 1
|
||||
|
||||
game = Node.void('game')
|
||||
game.add_child(Node.u32('gamecoin_packet', packet))
|
||||
game.add_child(Node.u32('gamecoin_block', block))
|
||||
game.add_child(Node.s8('result', result))
|
||||
game = Node.void("game")
|
||||
game.add_child(Node.u32("gamecoin_packet", packet))
|
||||
game.add_child(Node.u32("gamecoin_block", block))
|
||||
game.add_child(Node.s8("result", result))
|
||||
return game
|
||||
|
||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
|
||||
# Generic profile stuff
|
||||
game.add_child(Node.string('name', profile.get_str('name')))
|
||||
game.add_child(Node.string('code', ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
||||
game.add_child(Node.u32('gamecoin_block', profile.get_int('block')))
|
||||
game.add_child(Node.u32('exp_point', profile.get_int('exp')))
|
||||
game.add_child(Node.u32('m_user_cnt', profile.get_int('m_user_cnt')))
|
||||
game.add_child(Node.string("name", profile.get_str("name")))
|
||||
game.add_child(Node.string("code", ID.format_extid(profile.extid)))
|
||||
game.add_child(Node.u32("gamecoin_packet", profile.get_int("packet")))
|
||||
game.add_child(Node.u32("gamecoin_block", profile.get_int("block")))
|
||||
game.add_child(Node.u32("exp_point", profile.get_int("exp")))
|
||||
game.add_child(Node.u32("m_user_cnt", profile.get_int("m_user_cnt")))
|
||||
|
||||
game_config = self.get_game_config()
|
||||
if game_config.get_bool('force_unlock_cards'):
|
||||
game.add_child(Node.bool_array('have_item', [True] * 512))
|
||||
if game_config.get_bool("force_unlock_cards"):
|
||||
game.add_child(Node.bool_array("have_item", [True] * 512))
|
||||
else:
|
||||
game.add_child(Node.bool_array('have_item', [x > 0 for x in profile.get_int_array('have_item', 512)]))
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
game.add_child(Node.bool_array('have_note', [True] * 512))
|
||||
game.add_child(
|
||||
Node.bool_array(
|
||||
"have_item",
|
||||
[x > 0 for x in profile.get_int_array("have_item", 512)],
|
||||
)
|
||||
)
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
game.add_child(Node.bool_array("have_note", [True] * 512))
|
||||
else:
|
||||
game.add_child(Node.bool_array('have_note', [x > 0 for x in profile.get_int_array('have_note', 512)]))
|
||||
game.add_child(
|
||||
Node.bool_array(
|
||||
"have_note",
|
||||
[x > 0 for x in profile.get_int_array("have_note", 512)],
|
||||
)
|
||||
)
|
||||
|
||||
# Last played stuff
|
||||
lastdict = profile.get_dict('last')
|
||||
last = Node.void('last')
|
||||
lastdict = profile.get_dict("last")
|
||||
last = Node.void("last")
|
||||
game.add_child(last)
|
||||
last.set_attribute('music_id', str(lastdict.get_int('music_id')))
|
||||
last.set_attribute('music_type', str(lastdict.get_int('music_type')))
|
||||
last.set_attribute('sort_type', str(lastdict.get_int('sort_type')))
|
||||
last.set_attribute('headphone', str(lastdict.get_int('headphone')))
|
||||
last.set_attribute('hispeed', str(lastdict.get_int('hispeed')))
|
||||
last.set_attribute('appeal_id', str(lastdict.get_int('appeal_id')))
|
||||
last.set_attribute('frame0', str(lastdict.get_int('frame0')))
|
||||
last.set_attribute('frame1', str(lastdict.get_int('frame1')))
|
||||
last.set_attribute('frame2', str(lastdict.get_int('frame2')))
|
||||
last.set_attribute('frame3', str(lastdict.get_int('frame3')))
|
||||
last.set_attribute('frame4', str(lastdict.get_int('frame4')))
|
||||
last.set_attribute("music_id", str(lastdict.get_int("music_id")))
|
||||
last.set_attribute("music_type", str(lastdict.get_int("music_type")))
|
||||
last.set_attribute("sort_type", str(lastdict.get_int("sort_type")))
|
||||
last.set_attribute("headphone", str(lastdict.get_int("headphone")))
|
||||
last.set_attribute("hispeed", str(lastdict.get_int("hispeed")))
|
||||
last.set_attribute("appeal_id", str(lastdict.get_int("appeal_id")))
|
||||
last.set_attribute("frame0", str(lastdict.get_int("frame0")))
|
||||
last.set_attribute("frame1", str(lastdict.get_int("frame1")))
|
||||
last.set_attribute("frame2", str(lastdict.get_int("frame2")))
|
||||
last.set_attribute("frame3", str(lastdict.get_int("frame3")))
|
||||
last.set_attribute("frame4", str(lastdict.get_int("frame4")))
|
||||
|
||||
return game
|
||||
|
||||
def unformat_profile(self, userid: UserID, request: Node, oldprofile: Profile) -> Profile:
|
||||
def unformat_profile(
|
||||
self, userid: UserID, request: Node, oldprofile: Profile
|
||||
) -> Profile:
|
||||
newprofile = oldprofile.clone()
|
||||
|
||||
# Update experience and in-game currencies
|
||||
earned_gamecoin_packet = request.child_value('earned_gamecoin_packet')
|
||||
earned_gamecoin_packet = request.child_value("earned_gamecoin_packet")
|
||||
if earned_gamecoin_packet is not None:
|
||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
||||
newprofile.replace_int(
|
||||
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||
)
|
||||
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||
if earned_gamecoin_block is not None:
|
||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
||||
gain_exp = request.child_value('gain_exp')
|
||||
newprofile.replace_int(
|
||||
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||
)
|
||||
gain_exp = request.child_value("gain_exp")
|
||||
if gain_exp is not None:
|
||||
newprofile.replace_int('exp', newprofile.get_int('exp') + gain_exp)
|
||||
newprofile.replace_int("exp", newprofile.get_int("exp") + gain_exp)
|
||||
|
||||
# Miscelaneous stuff
|
||||
newprofile.replace_int('m_user_cnt', request.child_value('m_user_cnt'))
|
||||
newprofile.replace_int("m_user_cnt", request.child_value("m_user_cnt"))
|
||||
|
||||
# Update user's unlock status if we aren't force unlocked
|
||||
game_config = self.get_game_config()
|
||||
if not game_config.get_bool('force_unlock_cards'):
|
||||
have_item = request.child_value('have_item')
|
||||
if not game_config.get_bool("force_unlock_cards"):
|
||||
have_item = request.child_value("have_item")
|
||||
if have_item is not None:
|
||||
newprofile.replace_int_array('have_item', 512, [1 if x else 0 for x in have_item])
|
||||
if not game_config.get_bool('force_unlock_songs'):
|
||||
have_note = request.child_value('have_note')
|
||||
newprofile.replace_int_array(
|
||||
"have_item", 512, [1 if x else 0 for x in have_item]
|
||||
)
|
||||
if not game_config.get_bool("force_unlock_songs"):
|
||||
have_note = request.child_value("have_note")
|
||||
if have_note is not None:
|
||||
newprofile.replace_int_array('have_note', 512, [1 if x else 0 for x in have_note])
|
||||
newprofile.replace_int_array(
|
||||
"have_note", 512, [1 if x else 0 for x in have_note]
|
||||
)
|
||||
|
||||
# Grab last information.
|
||||
lastdict = newprofile.get_dict('last')
|
||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
||||
lastdict.replace_int('hispeed', request.child_value('hispeed'))
|
||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
||||
lastdict.replace_int('frame0', request.child_value('frame0'))
|
||||
lastdict.replace_int('frame1', request.child_value('frame1'))
|
||||
lastdict.replace_int('frame2', request.child_value('frame2'))
|
||||
lastdict.replace_int('frame3', request.child_value('frame3'))
|
||||
lastdict.replace_int('frame4', request.child_value('frame4'))
|
||||
last = request.child('last')
|
||||
lastdict = newprofile.get_dict("last")
|
||||
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||
lastdict.replace_int("hispeed", request.child_value("hispeed"))
|
||||
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||
lastdict.replace_int("frame0", request.child_value("frame0"))
|
||||
lastdict.replace_int("frame1", request.child_value("frame1"))
|
||||
lastdict.replace_int("frame2", request.child_value("frame2"))
|
||||
lastdict.replace_int("frame3", request.child_value("frame3"))
|
||||
lastdict.replace_int("frame4", request.child_value("frame4"))
|
||||
last = request.child("last")
|
||||
if last is not None:
|
||||
lastdict.replace_int('music_id', intish(last.attribute('music_id')))
|
||||
lastdict.replace_int('music_type', intish(last.attribute('music_type')))
|
||||
lastdict.replace_int('sort_type', intish(last.attribute('sort_type')))
|
||||
lastdict.replace_int("music_id", intish(last.attribute("music_id")))
|
||||
lastdict.replace_int("music_type", intish(last.attribute("music_type")))
|
||||
lastdict.replace_int("sort_type", intish(last.attribute("sort_type")))
|
||||
|
||||
# Save back last information gleaned from results
|
||||
newprofile.replace_dict('last', lastdict)
|
||||
newprofile.replace_dict("last", lastdict)
|
||||
|
||||
# Keep track of play statistics
|
||||
self.update_play_statistics(userid)
|
||||
|
@ -22,12 +22,17 @@ class SoundVoltexFactory(Factory):
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ['KFC']:
|
||||
for gamecode in ["KFC"]:
|
||||
Base.register(gamecode, SoundVoltexFactory)
|
||||
|
||||
@classmethod
|
||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
||||
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
def version_from_date(date: int) -> Optional[int]:
|
||||
if date < 2013060500:
|
||||
return VersionConstants.SDVX_BOOTH
|
||||
@ -39,14 +44,14 @@ class SoundVoltexFactory(Factory):
|
||||
return VersionConstants.SDVX_HEAVENLY_HAVEN
|
||||
return None
|
||||
|
||||
if model.gamecode == 'KFC':
|
||||
if model.gamecode == "KFC":
|
||||
if model.version is None:
|
||||
if parentmodel is None:
|
||||
return None
|
||||
|
||||
# We have no way to tell apart newer versions. However, we can make
|
||||
# an educated guess if we happen to be summoned for old profile lookup.
|
||||
if parentmodel.gamecode != 'KFC':
|
||||
if parentmodel.gamecode != "KFC":
|
||||
return None
|
||||
|
||||
parentversion = version_from_date(parentmodel.version)
|
||||
|
@ -14,7 +14,7 @@ class SoundVoltexGravityWars(
|
||||
SoundVoltexBase,
|
||||
):
|
||||
|
||||
name: str = 'SOUND VOLTEX III GRAVITY WARS'
|
||||
name: str = "SOUND VOLTEX III GRAVITY WARS"
|
||||
version: int = VersionConstants.SDVX_GRAVITY_WARS
|
||||
|
||||
GAME_LIMITED_LOCKED: Final[int] = 1
|
||||
@ -50,30 +50,30 @@ class SoundVoltexGravityWars(
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {
|
||||
'bools': [
|
||||
"bools": [
|
||||
{
|
||||
'name': 'Disable Online Matching',
|
||||
'tip': 'Disable online matching between games.',
|
||||
'category': 'game_config',
|
||||
'setting': 'disable_matching',
|
||||
"name": "Disable Online Matching",
|
||||
"tip": "Disable online matching between games.",
|
||||
"category": "game_config",
|
||||
"setting": "disable_matching",
|
||||
},
|
||||
{
|
||||
'name': 'Force Song Unlock',
|
||||
'tip': 'Force unlock all songs.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_songs',
|
||||
"name": "Force Song Unlock",
|
||||
"tip": "Force unlock all songs.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_songs",
|
||||
},
|
||||
{
|
||||
'name': 'Force Appeal Card Unlock',
|
||||
'tip': 'Force unlock all appeal cards.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_cards',
|
||||
"name": "Force Appeal Card Unlock",
|
||||
"tip": "Force unlock all appeal cards.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_cards",
|
||||
},
|
||||
{
|
||||
'name': 'Force Crew Card Unlock',
|
||||
'tip': 'Force unlock all crew and subcrew cards.',
|
||||
'category': 'game_config',
|
||||
'setting': 'force_unlock_crew',
|
||||
"name": "Force Crew Card Unlock",
|
||||
"tip": "Force unlock all crew and subcrew cards.",
|
||||
"category": "game_config",
|
||||
"setting": "force_unlock_crew",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -140,51 +140,54 @@ class SoundVoltexGravityWars(
|
||||
|
||||
def __get_skill_analyzer_skill_levels(self) -> Dict[int, str]:
|
||||
return {
|
||||
0: 'Skill LEVEL 01 岳翔',
|
||||
1: 'Skill LEVEL 02 流星',
|
||||
2: 'Skill LEVEL 03 月衝',
|
||||
3: 'Skill LEVEL 04 瞬光',
|
||||
4: 'Skill LEVEL 05 天極',
|
||||
5: 'Skill LEVEL 06 烈風',
|
||||
6: 'Skill LEVEL 07 雷電',
|
||||
7: 'Skill LEVEL 08 麗華',
|
||||
8: 'Skill LEVEL 09 魔騎士',
|
||||
9: 'Skill LEVEL 10 剛力羅',
|
||||
10: 'Skill LEVEL 11 或帝滅斗',
|
||||
11: 'Skill LEVEL ∞(12) 暴龍天',
|
||||
0: "Skill LEVEL 01 岳翔",
|
||||
1: "Skill LEVEL 02 流星",
|
||||
2: "Skill LEVEL 03 月衝",
|
||||
3: "Skill LEVEL 04 瞬光",
|
||||
4: "Skill LEVEL 05 天極",
|
||||
5: "Skill LEVEL 06 烈風",
|
||||
6: "Skill LEVEL 07 雷電",
|
||||
7: "Skill LEVEL 08 麗華",
|
||||
8: "Skill LEVEL 09 魔騎士",
|
||||
9: "Skill LEVEL 10 剛力羅",
|
||||
10: "Skill LEVEL 11 或帝滅斗",
|
||||
11: "Skill LEVEL ∞(12) 暴龍天",
|
||||
}
|
||||
|
||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
limited = Node.void('music_limited')
|
||||
game = Node.void("game_3")
|
||||
limited = Node.void("music_limited")
|
||||
game.add_child(limited)
|
||||
|
||||
# Song unlock config
|
||||
game_config = self.get_game_config()
|
||||
if game_config.get_bool('force_unlock_songs'):
|
||||
if game_config.get_bool("force_unlock_songs"):
|
||||
ids = set()
|
||||
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
||||
for song in songs:
|
||||
if song.data.get_int('limited') in (self.GAME_LIMITED_LOCKED, self.GAME_LIMITED_UNLOCKABLE):
|
||||
if song.data.get_int("limited") in (
|
||||
self.GAME_LIMITED_LOCKED,
|
||||
self.GAME_LIMITED_UNLOCKABLE,
|
||||
):
|
||||
ids.add((song.id, song.chart))
|
||||
|
||||
for (songid, chart) in ids:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
limited.add_child(info)
|
||||
info.add_child(Node.s32('music_id', songid))
|
||||
info.add_child(Node.u8('music_type', chart))
|
||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
||||
info.add_child(Node.s32("music_id", songid))
|
||||
info.add_child(Node.u8("music_type", chart))
|
||||
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||
|
||||
# Event config
|
||||
event = Node.void('event')
|
||||
event = Node.void("event")
|
||||
game.add_child(event)
|
||||
|
||||
def enable_event(eid: int) -> None:
|
||||
evt = Node.void('info')
|
||||
evt = Node.void("info")
|
||||
event.add_child(evt)
|
||||
evt.add_child(Node.u32('event_id', eid))
|
||||
evt.add_child(Node.u32("event_id", eid))
|
||||
|
||||
if not game_config.get_bool('disable_matching'):
|
||||
if not game_config.get_bool("disable_matching"):
|
||||
enable_event(1) # Matching enabled
|
||||
enable_event(2) # Floor Infection
|
||||
enable_event(3) # Policy Break
|
||||
@ -194,7 +197,7 @@ class SoundVoltexGravityWars(
|
||||
enable_event(eventid)
|
||||
|
||||
# Skill Analyzer config
|
||||
skill_course = Node.void('skill_course')
|
||||
skill_course = Node.void("skill_course")
|
||||
game.add_child(skill_course)
|
||||
|
||||
seasons = self._get_skill_analyzer_seasons()
|
||||
@ -202,74 +205,91 @@ class SoundVoltexGravityWars(
|
||||
courses = self._get_skill_analyzer_courses()
|
||||
max_level: Dict[int, int] = {}
|
||||
for course in courses:
|
||||
max_level[course['level']] = max(course['season_id'], max_level.get(course['level'], -1))
|
||||
max_level[course["level"]] = max(
|
||||
course["season_id"], max_level.get(course["level"], -1)
|
||||
)
|
||||
for course in courses:
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
skill_course.add_child(info)
|
||||
info.add_child(Node.s16('course_id', course.get('id', course['level'])))
|
||||
info.add_child(Node.s16('level', course['level']))
|
||||
info.add_child(Node.s32('season_id', course['season_id']))
|
||||
info.add_child(Node.string('season_name', seasons[course['season_id']]))
|
||||
info.add_child(Node.bool('season_new_flg', max_level[course['level']] == course['season_id']))
|
||||
info.add_child(Node.string('course_name', course.get('skill_name', skillnames.get(course['level'], ''))))
|
||||
info.add_child(Node.s16('course_type', 0))
|
||||
info.add_child(Node.s16('skill_name_id', course.get('skill_name_id', course['level'])))
|
||||
info.add_child(Node.bool('matching_assist', course['level'] >= 0 and course['level'] <= 6))
|
||||
info.add_child(Node.s16('gauge_type', self.GAME_GAUGE_TYPE_SKILL))
|
||||
info.add_child(Node.s16('paseli_type', 0))
|
||||
info.add_child(Node.s16("course_id", course.get("id", course["level"])))
|
||||
info.add_child(Node.s16("level", course["level"]))
|
||||
info.add_child(Node.s32("season_id", course["season_id"]))
|
||||
info.add_child(Node.string("season_name", seasons[course["season_id"]]))
|
||||
info.add_child(
|
||||
Node.bool(
|
||||
"season_new_flg", max_level[course["level"]] == course["season_id"]
|
||||
)
|
||||
)
|
||||
info.add_child(
|
||||
Node.string(
|
||||
"course_name",
|
||||
course.get("skill_name", skillnames.get(course["level"], "")),
|
||||
)
|
||||
)
|
||||
info.add_child(Node.s16("course_type", 0))
|
||||
info.add_child(
|
||||
Node.s16("skill_name_id", course.get("skill_name_id", course["level"]))
|
||||
)
|
||||
info.add_child(
|
||||
Node.bool(
|
||||
"matching_assist", course["level"] >= 0 and course["level"] <= 6
|
||||
)
|
||||
)
|
||||
info.add_child(Node.s16("gauge_type", self.GAME_GAUGE_TYPE_SKILL))
|
||||
info.add_child(Node.s16("paseli_type", 0))
|
||||
|
||||
for trackno, trackdata in enumerate(course['tracks']):
|
||||
track = Node.void('track')
|
||||
for trackno, trackdata in enumerate(course["tracks"]):
|
||||
track = Node.void("track")
|
||||
info.add_child(track)
|
||||
track.add_child(Node.s16('track_no', trackno))
|
||||
track.add_child(Node.s32('music_id', trackdata['id']))
|
||||
track.add_child(Node.s8('music_type', trackdata['type']))
|
||||
track.add_child(Node.s16("track_no", trackno))
|
||||
track.add_child(Node.s32("music_id", trackdata["id"]))
|
||||
track.add_child(Node.s8("music_type", trackdata["type"]))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_shop_request(self, request: Node) -> Node:
|
||||
self.update_machine_name(request.child_value('shopname'))
|
||||
self.update_machine_name(request.child_value("shopname"))
|
||||
|
||||
# Respond with number of milliseconds until next request
|
||||
game = Node.void('game_3')
|
||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
||||
game = Node.void("game_3")
|
||||
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||
return game
|
||||
|
||||
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
# Refresh interval in seconds.
|
||||
game.add_child(Node.u32('interval', 10))
|
||||
game.add_child(Node.u32("interval", 10))
|
||||
return game
|
||||
|
||||
def handle_game_3_entry_s_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game = Node.void("game_3")
|
||||
# This should be created on the fly for a lobby that we're in.
|
||||
game.add_child(Node.u32('entry_id', 1))
|
||||
game.add_child(Node.u32("entry_id", 1))
|
||||
return game
|
||||
|
||||
def handle_game_3_entry_e_request(self, request: Node) -> Node:
|
||||
# Lobby destroy method, eid node (u32) should be used
|
||||
# to destroy any open lobbies.
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_frozen_request(self, request: Node) -> Node:
|
||||
game = Node.void('game_3')
|
||||
game.add_child(Node.u8('result', 0))
|
||||
game = Node.void("game_3")
|
||||
game.add_child(Node.u8("result", 0))
|
||||
return game
|
||||
|
||||
def handle_game_3_save_e_request(self, request: Node) -> Node:
|
||||
# This has to do with Policy Break against ReflecBeat and
|
||||
# floor infection, but we don't implement multi-game support so meh.
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_play_e_request(self, request: Node) -> Node:
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_buy_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -283,15 +303,15 @@ class SoundVoltexGravityWars(
|
||||
|
||||
if userid is not None and profile is not None:
|
||||
# Look up packets and blocks
|
||||
packet = profile.get_int('packet')
|
||||
block = profile.get_int('block')
|
||||
packet = profile.get_int("packet")
|
||||
block = profile.get_int("block")
|
||||
|
||||
# Add on any additional we earned this round
|
||||
packet = packet + (request.child_value('earned_gamecoin_packet') or 0)
|
||||
block = block + (request.child_value('earned_gamecoin_block') or 0)
|
||||
packet = packet + (request.child_value("earned_gamecoin_packet") or 0)
|
||||
block = block + (request.child_value("earned_gamecoin_block") or 0)
|
||||
|
||||
currency_type = request.child_value('currency_type')
|
||||
price = request.child_value('item/price')
|
||||
currency_type = request.child_value("currency_type")
|
||||
price = request.child_value("item/price")
|
||||
if isinstance(price, list):
|
||||
# Sometimes we end up buying more than one item at once
|
||||
price = sum(price)
|
||||
@ -318,14 +338,14 @@ class SoundVoltexGravityWars(
|
||||
|
||||
if result == 0:
|
||||
# Transaction is valid, update the profile with new packets and blocks
|
||||
profile.replace_int('packet', packet)
|
||||
profile.replace_int('block', block)
|
||||
profile.replace_int("packet", packet)
|
||||
profile.replace_int("block", block)
|
||||
self.put_profile(userid, profile)
|
||||
|
||||
# If this was a song unlock, we should mark it as unlocked
|
||||
item_type = request.child_value('item/item_type')
|
||||
item_id = request.child_value('item/item_id')
|
||||
param = request.child_value('item/param')
|
||||
item_type = request.child_value("item/item_type")
|
||||
item_id = request.child_value("item/item_id")
|
||||
param = request.child_value("item/param")
|
||||
|
||||
if not isinstance(item_type, list):
|
||||
# Sometimes we buy multiple things at once. Make it easier by always assuming this.
|
||||
@ -339,9 +359,9 @@ class SoundVoltexGravityWars(
|
||||
self.version,
|
||||
userid,
|
||||
item_id[i],
|
||||
f'item_{item_type[i]}',
|
||||
f"item_{item_type[i]}",
|
||||
{
|
||||
'param': param[i],
|
||||
"param": param[i],
|
||||
},
|
||||
)
|
||||
|
||||
@ -351,23 +371,23 @@ class SoundVoltexGravityWars(
|
||||
block = 0
|
||||
result = 1
|
||||
|
||||
game = Node.void('game_3')
|
||||
game.add_child(Node.u32('gamecoin_packet', packet))
|
||||
game.add_child(Node.u32('gamecoin_block', block))
|
||||
game.add_child(Node.s8('result', result))
|
||||
game = Node.void("game_3")
|
||||
game.add_child(Node.u32("gamecoin_packet", packet))
|
||||
game.add_child(Node.u32("gamecoin_block", block))
|
||||
game.add_child(Node.s8("result", result))
|
||||
return game
|
||||
|
||||
def handle_game_3_new_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
name = request.child_value('name')
|
||||
loc = ID.parse_machine_id(request.child_value('locid'))
|
||||
refid = request.child_value("refid")
|
||||
name = request.child_value("name")
|
||||
loc = ID.parse_machine_id(request.child_value("locid"))
|
||||
self.new_profile_by_refid(refid, name, loc)
|
||||
|
||||
root = Node.void('game_3')
|
||||
root = Node.void("game_3")
|
||||
return root
|
||||
|
||||
def handle_game_3_load_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
root = self.get_profile_by_refid(refid)
|
||||
if root is not None:
|
||||
return root
|
||||
@ -386,17 +406,17 @@ class SoundVoltexGravityWars(
|
||||
profile = None
|
||||
|
||||
if profile is not None:
|
||||
root = Node.void('game_3')
|
||||
root.add_child(Node.u8('result', 2))
|
||||
root.add_child(Node.string('name', profile.get_str('name')))
|
||||
root = Node.void("game_3")
|
||||
root.add_child(Node.u8("result", 2))
|
||||
root.add_child(Node.string("name", profile.get_str("name")))
|
||||
return root
|
||||
else:
|
||||
root = Node.void('game_3')
|
||||
root.add_child(Node.u8('result', 1))
|
||||
root = Node.void("game_3")
|
||||
root.add_child(Node.u8("result", 1))
|
||||
return root
|
||||
|
||||
def handle_game_3_save_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -412,10 +432,10 @@ class SoundVoltexGravityWars(
|
||||
if userid is not None and newprofile is not None:
|
||||
self.put_profile(userid, newprofile)
|
||||
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_load_m_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('dataid')
|
||||
refid = request.child_value("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -428,28 +448,37 @@ class SoundVoltexGravityWars(
|
||||
scores = []
|
||||
|
||||
# Output to the game
|
||||
game = Node.void('game_3')
|
||||
new = Node.void('new')
|
||||
game = Node.void("game_3")
|
||||
new = Node.void("new")
|
||||
game.add_child(new)
|
||||
|
||||
for score in scores:
|
||||
music = Node.void('music')
|
||||
music = Node.void("music")
|
||||
new.add_child(music)
|
||||
music.add_child(Node.u32('music_id', score.id))
|
||||
music.add_child(Node.u32('music_type', score.chart))
|
||||
music.add_child(Node.u32('score', score.points))
|
||||
music.add_child(Node.u32('cnt', score.plays))
|
||||
music.add_child(Node.u32('clear_type', self.__db_to_game_clear_type(score.data.get_int('clear_type'))))
|
||||
music.add_child(Node.u32('score_grade', self.__db_to_game_grade(score.data.get_int('grade'))))
|
||||
stats = score.data.get_dict('stats')
|
||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
||||
music.add_child(Node.u32('long_rate', stats.get_int('long_rate')))
|
||||
music.add_child(Node.u32('vol_rate', stats.get_int('vol_rate')))
|
||||
music.add_child(Node.u32("music_id", score.id))
|
||||
music.add_child(Node.u32("music_type", score.chart))
|
||||
music.add_child(Node.u32("score", score.points))
|
||||
music.add_child(Node.u32("cnt", score.plays))
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"clear_type",
|
||||
self.__db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||
)
|
||||
)
|
||||
music.add_child(
|
||||
Node.u32(
|
||||
"score_grade", self.__db_to_game_grade(score.data.get_int("grade"))
|
||||
)
|
||||
)
|
||||
stats = score.data.get_dict("stats")
|
||||
music.add_child(Node.u32("btn_rate", stats.get_int("btn_rate")))
|
||||
music.add_child(Node.u32("long_rate", stats.get_int("long_rate")))
|
||||
music.add_child(Node.u32("vol_rate", stats.get_int("vol_rate")))
|
||||
|
||||
return game
|
||||
|
||||
def handle_game_3_save_m_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('refid')
|
||||
refid = request.child_value("refid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -457,19 +486,19 @@ class SoundVoltexGravityWars(
|
||||
userid = None
|
||||
|
||||
# Doesn't matter if userid is None here, that's an anonymous score
|
||||
musicid = request.child_value('music_id')
|
||||
chart = request.child_value('music_type')
|
||||
points = request.child_value('score')
|
||||
combo = request.child_value('max_chain')
|
||||
clear_type = self.__game_to_db_clear_type(request.child_value('clear_type'))
|
||||
grade = self.__game_to_db_grade(request.child_value('score_grade'))
|
||||
musicid = request.child_value("music_id")
|
||||
chart = request.child_value("music_type")
|
||||
points = request.child_value("score")
|
||||
combo = request.child_value("max_chain")
|
||||
clear_type = self.__game_to_db_clear_type(request.child_value("clear_type"))
|
||||
grade = self.__game_to_db_grade(request.child_value("score_grade"))
|
||||
stats = {
|
||||
'btn_rate': request.child_value('btn_rate'),
|
||||
'long_rate': request.child_value('long_rate'),
|
||||
'vol_rate': request.child_value('vol_rate'),
|
||||
'critical': request.child_value('critical'),
|
||||
'near': request.child_value('near'),
|
||||
'error': request.child_value('error'),
|
||||
"btn_rate": request.child_value("btn_rate"),
|
||||
"long_rate": request.child_value("long_rate"),
|
||||
"vol_rate": request.child_value("vol_rate"),
|
||||
"critical": request.child_value("critical"),
|
||||
"near": request.child_value("near"),
|
||||
"error": request.child_value("error"),
|
||||
}
|
||||
|
||||
# Save the score
|
||||
@ -485,10 +514,10 @@ class SoundVoltexGravityWars(
|
||||
)
|
||||
|
||||
# Return a blank response
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
||||
def handle_game_3_save_c_request(self, request: Node) -> Node:
|
||||
refid = request.child_value('dataid')
|
||||
refid = request.child_value("dataid")
|
||||
|
||||
if refid is not None:
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
@ -496,27 +525,29 @@ class SoundVoltexGravityWars(
|
||||
userid = None
|
||||
|
||||
if userid is not None:
|
||||
course_id = request.child_value('crsid')
|
||||
clear_type = request.child_value('ct')
|
||||
achievement_rate = request.child_value('ar')
|
||||
season_id = request.child_value('ssnid')
|
||||
course_id = request.child_value("crsid")
|
||||
clear_type = request.child_value("ct")
|
||||
achievement_rate = request.child_value("ar")
|
||||
season_id = request.child_value("ssnid")
|
||||
|
||||
# Do not update the course achievement when old achievement rate is greater.
|
||||
old = self.data.local.user.get_achievement(self.game, self.version, userid, (season_id * 100) + course_id, 'course')
|
||||
if old is not None and old.get_int('achievement_rate') > achievement_rate:
|
||||
return Node.void('game_3')
|
||||
old = self.data.local.user.get_achievement(
|
||||
self.game, self.version, userid, (season_id * 100) + course_id, "course"
|
||||
)
|
||||
if old is not None and old.get_int("achievement_rate") > achievement_rate:
|
||||
return Node.void("game_3")
|
||||
|
||||
self.data.local.user.put_achievement(
|
||||
self.game,
|
||||
self.version,
|
||||
userid,
|
||||
(season_id * 100) + course_id,
|
||||
'course',
|
||||
"course",
|
||||
{
|
||||
'clear_type': clear_type,
|
||||
'achievement_rate': achievement_rate,
|
||||
"clear_type": clear_type,
|
||||
"achievement_rate": achievement_rate,
|
||||
},
|
||||
)
|
||||
|
||||
# Return a blank response
|
||||
return Node.void('game_3')
|
||||
return Node.void("game_3")
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,12 @@ class BaseClient:
|
||||
CARD_BAD_PIN: Final[int] = 116
|
||||
CARD_NOT_ALLOWED: Final[int] = 110
|
||||
|
||||
CORRECT_PASSWORD: Final[str] = '1234'
|
||||
WRONG_PASSWORD: Final[str] = '4321'
|
||||
CORRECT_PASSWORD: Final[str] = "1234"
|
||||
WRONG_PASSWORD: Final[str] = "4321"
|
||||
|
||||
def __init__(self, proto: ClientProtocol, pcbid: str, config: Dict[str, Any]) -> None:
|
||||
def __init__(
|
||||
self, proto: ClientProtocol, pcbid: str, config: Dict[str, Any]
|
||||
) -> None:
|
||||
self.__proto = proto
|
||||
self.pcbid = pcbid
|
||||
self.config = config
|
||||
@ -31,15 +33,15 @@ class BaseClient:
|
||||
return "E004" + random_hex_string(12, caps=True)
|
||||
|
||||
def call_node(self) -> Node:
|
||||
call = Node.void('call')
|
||||
call.set_attribute('model', self.config['model'])
|
||||
call.set_attribute('srcid', self.pcbid)
|
||||
call.set_attribute('tag', random_hex_string(8))
|
||||
call = Node.void("call")
|
||||
call.set_attribute("model", self.config["model"])
|
||||
call.set_attribute("srcid", self.pcbid)
|
||||
call.set_attribute("tag", random_hex_string(8))
|
||||
return call
|
||||
|
||||
def exchange(self, path: str, tree: Node) -> Node:
|
||||
module = tree.children[0].name
|
||||
method = tree.children[0].attribute('method')
|
||||
method = tree.children[0].attribute("method")
|
||||
|
||||
return self.__proto.exchange(
|
||||
f'{path}?model={self.config["model"]}&module={module}&method={method}',
|
||||
@ -47,12 +49,12 @@ class BaseClient:
|
||||
)
|
||||
|
||||
def __assert_path(self, root: Node, path: str) -> bool:
|
||||
parts = path.split('/')
|
||||
parts = path.split("/")
|
||||
children = [root]
|
||||
node: Optional[Node] = None
|
||||
|
||||
for part in parts:
|
||||
if part[0] == '@':
|
||||
if part[0] == "@":
|
||||
# Verify attribute, should be last part in chain so
|
||||
# assume its the first node
|
||||
if node is None:
|
||||
@ -88,77 +90,79 @@ class BaseClient:
|
||||
"""
|
||||
|
||||
if not self.__assert_path(root, path):
|
||||
raise Exception(f'Path \'{path}\' not found in root node:\n{root}')
|
||||
raise Exception(f"Path '{path}' not found in root node:\n{root}")
|
||||
|
||||
def verify_services_get(self, expected_services: List[str]=[], include_net: bool = False) -> None:
|
||||
def verify_services_get(
|
||||
self, expected_services: List[str] = [], include_net: bool = False
|
||||
) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
services = Node.void('services')
|
||||
services = Node.void("services")
|
||||
call.add_child(services)
|
||||
services.set_attribute('method', 'get')
|
||||
services.set_attribute("method", "get")
|
||||
|
||||
if self.config['avs'] is not None:
|
||||
if self.config["avs"] is not None:
|
||||
# Some older games don't include this info
|
||||
info = Node.void('info')
|
||||
info = Node.void("info")
|
||||
services.add_child(info)
|
||||
|
||||
info.add_child(Node.string('AVS2', self.config['avs']))
|
||||
info.add_child(Node.string("AVS2", self.config["avs"]))
|
||||
|
||||
if include_net:
|
||||
net = Node.void('net')
|
||||
net = Node.void("net")
|
||||
services.add_child(net)
|
||||
iface = Node.void('if')
|
||||
iface = Node.void("if")
|
||||
net.add_child(iface)
|
||||
iface.add_child(Node.u8('id', 0))
|
||||
iface.add_child(Node.bool('valid', True))
|
||||
iface.add_child(Node.u8('type', 1))
|
||||
iface.add_child(Node.u8_array('mac', [1, 2, 3, 4, 5, 6]))
|
||||
iface.add_child(Node.ipv4('addr', '10.0.0.100'))
|
||||
iface.add_child(Node.ipv4('bcast', '10.0.0.255'))
|
||||
iface.add_child(Node.ipv4('netmask', '255.255.255.0'))
|
||||
iface.add_child(Node.ipv4('gateway', '10.0.0.1'))
|
||||
iface.add_child(Node.ipv4('dhcp', '10.0.0.1'))
|
||||
iface.add_child(Node.u8("id", 0))
|
||||
iface.add_child(Node.bool("valid", True))
|
||||
iface.add_child(Node.u8("type", 1))
|
||||
iface.add_child(Node.u8_array("mac", [1, 2, 3, 4, 5, 6]))
|
||||
iface.add_child(Node.ipv4("addr", "10.0.0.100"))
|
||||
iface.add_child(Node.ipv4("bcast", "10.0.0.255"))
|
||||
iface.add_child(Node.ipv4("netmask", "255.255.255.0"))
|
||||
iface.add_child(Node.ipv4("gateway", "10.0.0.1"))
|
||||
iface.add_child(Node.ipv4("dhcp", "10.0.0.1"))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/services', call)
|
||||
resp = self.exchange("core/services", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/services")
|
||||
items = resp.child('services').children
|
||||
items = resp.child("services").children
|
||||
|
||||
returned_services = []
|
||||
for item in items:
|
||||
# Make sure it is an item with a url component
|
||||
self.assert_path(item, 'item/@url')
|
||||
self.assert_path(item, "item/@url")
|
||||
|
||||
# Get list of services provided
|
||||
returned_services.append(item.attribute('name'))
|
||||
returned_services.append(item.attribute("name"))
|
||||
|
||||
for service in expected_services:
|
||||
if service not in returned_services:
|
||||
raise Exception(f'Service \'{service}\' expected but not returned')
|
||||
raise Exception(f"Service '{service}' expected but not returned")
|
||||
|
||||
def verify_pcbtracker_alive(self, ecflag: int = 1) -> bool:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
pcbtracker = Node.void('pcbtracker')
|
||||
pcbtracker = Node.void("pcbtracker")
|
||||
call.add_child(pcbtracker)
|
||||
pcbtracker.set_attribute('accountid', self.pcbid)
|
||||
pcbtracker.set_attribute('ecflag', str(ecflag))
|
||||
pcbtracker.set_attribute('hardid', '01000027584F6D3A')
|
||||
pcbtracker.set_attribute('method', 'alive')
|
||||
pcbtracker.set_attribute('softid', '00010203040506070809')
|
||||
pcbtracker.set_attribute("accountid", self.pcbid)
|
||||
pcbtracker.set_attribute("ecflag", str(ecflag))
|
||||
pcbtracker.set_attribute("hardid", "01000027584F6D3A")
|
||||
pcbtracker.set_attribute("method", "alive")
|
||||
pcbtracker.set_attribute("softid", "00010203040506070809")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/pcbtracker', call)
|
||||
resp = self.exchange("core/pcbtracker", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/pcbtracker/@ecenable")
|
||||
|
||||
# Print out setting
|
||||
enable = int(resp.child('pcbtracker').attribute('ecenable'))
|
||||
enable = int(resp.child("pcbtracker").attribute("ecenable"))
|
||||
if enable != 0:
|
||||
return True
|
||||
return False
|
||||
@ -167,12 +171,12 @@ class BaseClient:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
message = Node.void('message')
|
||||
message = Node.void("message")
|
||||
call.add_child(message)
|
||||
message.set_attribute('method', 'get')
|
||||
message.set_attribute("method", "get")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/message', call)
|
||||
resp = self.exchange("core/message", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/message/@status")
|
||||
@ -181,13 +185,13 @@ class BaseClient:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
dlstatus = Node.void('dlstatus')
|
||||
dlstatus = Node.void("dlstatus")
|
||||
call.add_child(dlstatus)
|
||||
dlstatus.set_attribute('method', 'progress')
|
||||
dlstatus.add_child(Node.s32('progress', 0))
|
||||
dlstatus.set_attribute("method", "progress")
|
||||
dlstatus.add_child(Node.s32("progress", 0))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/dlstatus', call)
|
||||
resp = self.exchange("core/dlstatus", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/dlstatus/@status")
|
||||
@ -196,28 +200,28 @@ class BaseClient:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
package = Node.void('package')
|
||||
package = Node.void("package")
|
||||
call.add_child(package)
|
||||
package.set_attribute('method', 'list')
|
||||
package.set_attribute('pkgtype', 'all')
|
||||
package.set_attribute("method", "list")
|
||||
package.set_attribute("pkgtype", "all")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/package', call)
|
||||
resp = self.exchange("core/package", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/package")
|
||||
|
||||
def verify_facility_get(self, encoding: str='SHIFT_JIS') -> str:
|
||||
def verify_facility_get(self, encoding: str = "SHIFT_JIS") -> str:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
facility = Node.void('facility')
|
||||
facility = Node.void("facility")
|
||||
call.add_child(facility)
|
||||
facility.set_attribute('encoding', encoding)
|
||||
facility.set_attribute('method', 'get')
|
||||
facility.set_attribute("encoding", encoding)
|
||||
facility.set_attribute("method", "get")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/facility', call)
|
||||
resp = self.exchange("core/facility", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/facility/location/id")
|
||||
@ -226,199 +230,220 @@ class BaseClient:
|
||||
self.assert_path(resp, "response/facility/public")
|
||||
self.assert_path(resp, "response/facility/share")
|
||||
|
||||
return resp.child_value('facility/location/id')
|
||||
return resp.child_value("facility/location/id")
|
||||
|
||||
def verify_pcbevent_put(self) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
pcbevent = Node.void('pcbevent')
|
||||
pcbevent = Node.void("pcbevent")
|
||||
call.add_child(pcbevent)
|
||||
pcbevent.set_attribute('method', 'put')
|
||||
pcbevent.add_child(Node.time('time', int(time.time())))
|
||||
pcbevent.add_child(Node.u32('seq', 0))
|
||||
pcbevent.set_attribute("method", "put")
|
||||
pcbevent.add_child(Node.time("time", int(time.time())))
|
||||
pcbevent.add_child(Node.u32("seq", 0))
|
||||
|
||||
item = Node.void('item')
|
||||
item = Node.void("item")
|
||||
pcbevent.add_child(item)
|
||||
item.add_child(Node.string('name', 'boot'))
|
||||
item.add_child(Node.s32('value', 1))
|
||||
item.add_child(Node.time('time', int(time.time())))
|
||||
item.add_child(Node.string("name", "boot"))
|
||||
item.add_child(Node.s32("value", 1))
|
||||
item.add_child(Node.time("time", int(time.time())))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/pcbevent', call)
|
||||
resp = self.exchange("core/pcbevent", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/pcbevent")
|
||||
|
||||
def verify_cardmng_inquire(self, card_id: str, msg_type: str, paseli_enabled: bool) -> Optional[str]:
|
||||
def verify_cardmng_inquire(
|
||||
self, card_id: str, msg_type: str, paseli_enabled: bool
|
||||
) -> Optional[str]:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
cardmng = Node.void('cardmng')
|
||||
cardmng = Node.void("cardmng")
|
||||
call.add_child(cardmng)
|
||||
cardmng.set_attribute('cardid', card_id)
|
||||
cardmng.set_attribute('cardtype', '1')
|
||||
cardmng.set_attribute('method', 'inquire')
|
||||
cardmng.set_attribute('update', '0')
|
||||
if msg_type == 'new' and 'old_profile_model' in self.config:
|
||||
cardmng.set_attribute('model', self.config['old_profile_model'])
|
||||
cardmng.set_attribute("cardid", card_id)
|
||||
cardmng.set_attribute("cardtype", "1")
|
||||
cardmng.set_attribute("method", "inquire")
|
||||
cardmng.set_attribute("update", "0")
|
||||
if msg_type == "new" and "old_profile_model" in self.config:
|
||||
cardmng.set_attribute("model", self.config["old_profile_model"])
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/cardmng', call)
|
||||
resp = self.exchange("core/cardmng", call)
|
||||
|
||||
if msg_type == 'unregistered':
|
||||
if msg_type == "unregistered":
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/cardmng/@status")
|
||||
|
||||
# Verify that we weren't found
|
||||
status = int(resp.child('cardmng').attribute('status'))
|
||||
status = int(resp.child("cardmng").attribute("status"))
|
||||
if status != self.CARD_NEW:
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid status \'{status}\'')
|
||||
raise Exception(f"Card '{card_id}' returned invalid status '{status}'")
|
||||
|
||||
# Nothing to return
|
||||
return None
|
||||
elif msg_type == 'new':
|
||||
elif msg_type == "new":
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/cardmng/@refid")
|
||||
self.assert_path(resp, "response/cardmng/@binded")
|
||||
self.assert_path(resp, "response/cardmng/@newflag")
|
||||
self.assert_path(resp, "response/cardmng/@ecflag")
|
||||
|
||||
binded = int(resp.child('cardmng').attribute('binded'))
|
||||
newflag = int(resp.child('cardmng').attribute('newflag'))
|
||||
ecflag = int(resp.child('cardmng').attribute('ecflag'))
|
||||
binded = int(resp.child("cardmng").attribute("binded"))
|
||||
newflag = int(resp.child("cardmng").attribute("newflag"))
|
||||
ecflag = int(resp.child("cardmng").attribute("ecflag"))
|
||||
|
||||
if binded != 0:
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid binded value \'{binded}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid binded value '{binded}'"
|
||||
)
|
||||
if newflag != 1:
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid newflag value \'{newflag}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid newflag value '{newflag}'"
|
||||
)
|
||||
if ecflag != (1 if paseli_enabled else 0):
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid ecflag value \'{newflag}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid ecflag value '{newflag}'"
|
||||
)
|
||||
|
||||
# Return the refid
|
||||
return resp.child('cardmng').attribute('refid')
|
||||
elif msg_type == 'query':
|
||||
return resp.child("cardmng").attribute("refid")
|
||||
elif msg_type == "query":
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/cardmng/@refid")
|
||||
self.assert_path(resp, "response/cardmng/@binded")
|
||||
self.assert_path(resp, "response/cardmng/@newflag")
|
||||
self.assert_path(resp, "response/cardmng/@ecflag")
|
||||
|
||||
binded = int(resp.child('cardmng').attribute('binded'))
|
||||
newflag = int(resp.child('cardmng').attribute('newflag'))
|
||||
ecflag = int(resp.child('cardmng').attribute('ecflag'))
|
||||
binded = int(resp.child("cardmng").attribute("binded"))
|
||||
newflag = int(resp.child("cardmng").attribute("newflag"))
|
||||
ecflag = int(resp.child("cardmng").attribute("ecflag"))
|
||||
|
||||
if binded != 1:
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid binded value \'{binded}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid binded value '{binded}'"
|
||||
)
|
||||
if newflag != 0:
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid newflag value \'{newflag}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid newflag value '{newflag}'"
|
||||
)
|
||||
if ecflag != (1 if paseli_enabled else 0):
|
||||
raise Exception(f'Card \'{card_id}\' returned invalid ecflag value \'{newflag}\'')
|
||||
raise Exception(
|
||||
f"Card '{card_id}' returned invalid ecflag value '{newflag}'"
|
||||
)
|
||||
|
||||
# Return the refid
|
||||
return resp.child('cardmng').attribute('refid')
|
||||
return resp.child("cardmng").attribute("refid")
|
||||
else:
|
||||
raise Exception(f'Unrecognized message type \'{msg_type}\'')
|
||||
raise Exception(f"Unrecognized message type '{msg_type}'")
|
||||
|
||||
def verify_cardmng_getrefid(self, card_id: str) -> str:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
cardmng = Node.void('cardmng')
|
||||
cardmng = Node.void("cardmng")
|
||||
call.add_child(cardmng)
|
||||
cardmng.set_attribute('cardid', card_id)
|
||||
cardmng.set_attribute('cardtype', '1')
|
||||
cardmng.set_attribute('method', 'getrefid')
|
||||
cardmng.set_attribute('newflag', '0')
|
||||
cardmng.set_attribute('passwd', self.CORRECT_PASSWORD)
|
||||
cardmng.set_attribute("cardid", card_id)
|
||||
cardmng.set_attribute("cardtype", "1")
|
||||
cardmng.set_attribute("method", "getrefid")
|
||||
cardmng.set_attribute("newflag", "0")
|
||||
cardmng.set_attribute("passwd", self.CORRECT_PASSWORD)
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/cardmng', call)
|
||||
resp = self.exchange("core/cardmng", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/cardmng/@refid")
|
||||
|
||||
return resp.child('cardmng').attribute('refid')
|
||||
return resp.child("cardmng").attribute("refid")
|
||||
|
||||
def verify_cardmng_authpass(self, ref_id: str, correct: bool) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
cardmng = Node.void('cardmng')
|
||||
cardmng = Node.void("cardmng")
|
||||
call.add_child(cardmng)
|
||||
cardmng.set_attribute('method', 'authpass')
|
||||
cardmng.set_attribute('pass', self.CORRECT_PASSWORD if correct else self.CORRECT_PASSWORD[::-1])
|
||||
cardmng.set_attribute('refid', ref_id)
|
||||
cardmng.set_attribute("method", "authpass")
|
||||
cardmng.set_attribute(
|
||||
"pass", self.CORRECT_PASSWORD if correct else self.CORRECT_PASSWORD[::-1]
|
||||
)
|
||||
cardmng.set_attribute("refid", ref_id)
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/cardmng', call)
|
||||
resp = self.exchange("core/cardmng", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/cardmng/@status")
|
||||
|
||||
status = int(resp.child('cardmng').attribute('status'))
|
||||
status = int(resp.child("cardmng").attribute("status"))
|
||||
if status != (self.CARD_OK if correct else self.CARD_BAD_PIN):
|
||||
raise Exception(f'Ref ID \'{ref_id}\' returned invalid status \'{status}\'')
|
||||
raise Exception(f"Ref ID '{ref_id}' returned invalid status '{status}'")
|
||||
|
||||
def verify_eacoin_checkin(self, card_id: str) -> Tuple[str, int]:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
eacoin = Node.void('eacoin')
|
||||
eacoin = Node.void("eacoin")
|
||||
call.add_child(eacoin)
|
||||
eacoin.set_attribute('method', 'checkin')
|
||||
eacoin.add_child(Node.string('cardtype', '1'))
|
||||
eacoin.add_child(Node.string('cardid', card_id))
|
||||
eacoin.add_child(Node.string('passwd', self.CORRECT_PASSWORD))
|
||||
eacoin.add_child(Node.string('ectype', '1'))
|
||||
eacoin.set_attribute("method", "checkin")
|
||||
eacoin.add_child(Node.string("cardtype", "1"))
|
||||
eacoin.add_child(Node.string("cardid", card_id))
|
||||
eacoin.add_child(Node.string("passwd", self.CORRECT_PASSWORD))
|
||||
eacoin.add_child(Node.string("ectype", "1"))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/eacoin', call)
|
||||
resp = self.exchange("core/eacoin", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/eacoin/sessid")
|
||||
self.assert_path(resp, "response/eacoin/balance")
|
||||
|
||||
return (resp.child('eacoin').child_value('sessid'), resp.child('eacoin').child_value('balance'))
|
||||
return (
|
||||
resp.child("eacoin").child_value("sessid"),
|
||||
resp.child("eacoin").child_value("balance"),
|
||||
)
|
||||
|
||||
def verify_eacoin_consume(self, sessid: str, balance: int, amount: int) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
eacoin = Node.void('eacoin')
|
||||
eacoin = Node.void("eacoin")
|
||||
call.add_child(eacoin)
|
||||
eacoin.set_attribute('method', 'consume')
|
||||
eacoin.add_child(Node.string('sessid', sessid))
|
||||
eacoin.add_child(Node.s16('sequence', 0))
|
||||
eacoin.add_child(Node.s32('payment', amount))
|
||||
eacoin.add_child(Node.s32('service', 0))
|
||||
eacoin.add_child(Node.string('itemtype', '0'))
|
||||
eacoin.add_child(Node.string('detail', '/eacoin/start_pt1'))
|
||||
eacoin.set_attribute("method", "consume")
|
||||
eacoin.add_child(Node.string("sessid", sessid))
|
||||
eacoin.add_child(Node.s16("sequence", 0))
|
||||
eacoin.add_child(Node.s32("payment", amount))
|
||||
eacoin.add_child(Node.s32("service", 0))
|
||||
eacoin.add_child(Node.string("itemtype", "0"))
|
||||
eacoin.add_child(Node.string("detail", "/eacoin/start_pt1"))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/eacoin', call)
|
||||
resp = self.exchange("core/eacoin", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/eacoin/balance")
|
||||
|
||||
newbalance = resp.child('eacoin').child_value('balance')
|
||||
newbalance = resp.child("eacoin").child_value("balance")
|
||||
if balance - amount != newbalance:
|
||||
raise Exception(f"Expected to get back balance {balance - amount} but got {newbalance}")
|
||||
raise Exception(
|
||||
f"Expected to get back balance {balance - amount} but got {newbalance}"
|
||||
)
|
||||
|
||||
def verify_eacoin_checkout(self, session: str) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
eacoin = Node.void('eacoin')
|
||||
eacoin = Node.void("eacoin")
|
||||
call.add_child(eacoin)
|
||||
eacoin.set_attribute('method', 'checkout')
|
||||
eacoin.add_child(Node.string('sessid', session))
|
||||
eacoin.set_attribute("method", "checkout")
|
||||
eacoin.add_child(Node.string("sessid", session))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('core/eacoin', call)
|
||||
resp = self.exchange("core/eacoin", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/eacoin/@status")
|
||||
|
||||
def verify(self, cardid: Optional[str]) -> None:
|
||||
raise Exception('Override in subclass!')
|
||||
raise Exception("Override in subclass!")
|
||||
|
@ -7,30 +7,30 @@ from bemani.protocol import Node
|
||||
|
||||
|
||||
class TheStarBishiBashiClient(BaseClient):
|
||||
NAME = 'TEST'
|
||||
NAME = "TEST"
|
||||
|
||||
def verify_eventlog_write(self, location: str) -> None:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
eventlog = Node.void('eventlog')
|
||||
eventlog = Node.void("eventlog")
|
||||
call.add_child(eventlog)
|
||||
eventlog.set_attribute('method', 'write')
|
||||
eventlog.add_child(Node.u32('retrycnt', 0))
|
||||
data = Node.void('data')
|
||||
eventlog.set_attribute("method", "write")
|
||||
eventlog.add_child(Node.u32("retrycnt", 0))
|
||||
data = Node.void("data")
|
||||
eventlog.add_child(data)
|
||||
data.add_child(Node.string('eventid', 'S_PWRON'))
|
||||
data.add_child(Node.s32('eventorder', 0))
|
||||
data.add_child(Node.u64('pcbtime', int(time.time() * 1000)))
|
||||
data.add_child(Node.s64('gamesession', -1))
|
||||
data.add_child(Node.string('strdata1', '1.7.6'))
|
||||
data.add_child(Node.string('strdata2', ''))
|
||||
data.add_child(Node.s64('numdata1', 1))
|
||||
data.add_child(Node.s64('numdata2', 0))
|
||||
data.add_child(Node.string('locationid', location))
|
||||
data.add_child(Node.string("eventid", "S_PWRON"))
|
||||
data.add_child(Node.s32("eventorder", 0))
|
||||
data.add_child(Node.u64("pcbtime", int(time.time() * 1000)))
|
||||
data.add_child(Node.s64("gamesession", -1))
|
||||
data.add_child(Node.string("strdata1", "1.7.6"))
|
||||
data.add_child(Node.string("strdata2", ""))
|
||||
data.add_child(Node.s64("numdata1", 1))
|
||||
data.add_child(Node.s64("numdata2", 0))
|
||||
data.add_child(Node.string("locationid", location))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/eventlog/gamesession")
|
||||
@ -42,17 +42,17 @@ class TheStarBishiBashiClient(BaseClient):
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
system = Node.void('system')
|
||||
system = Node.void("system")
|
||||
call.add_child(system)
|
||||
system.set_attribute('method', 'getmaster')
|
||||
data = Node.void('data')
|
||||
system.set_attribute("method", "getmaster")
|
||||
data = Node.void("data")
|
||||
system.add_child(data)
|
||||
data.add_child(Node.string('gamekind', 'IBB'))
|
||||
data.add_child(Node.string('datatype', 'S_SRVMSG'))
|
||||
data.add_child(Node.string('datakey', 'INFO'))
|
||||
data.add_child(Node.string("gamekind", "IBB"))
|
||||
data.add_child(Node.string("datatype", "S_SRVMSG"))
|
||||
data.add_child(Node.string("datakey", "INFO"))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/system/result")
|
||||
@ -62,116 +62,116 @@ class TheStarBishiBashiClient(BaseClient):
|
||||
|
||||
# Set up profile write
|
||||
profiledata = [
|
||||
b'ffffffff',
|
||||
b'IBBDAT00',
|
||||
b'1',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'e474c1b',
|
||||
b'0',
|
||||
b'0',
|
||||
b'ff',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'0.000000',
|
||||
b'',
|
||||
b'',
|
||||
b'',
|
||||
b'\x96\xa2\x90\xdd\x92\xe8',
|
||||
b'\x8d\x81\x8d`',
|
||||
b'',
|
||||
b'',
|
||||
b'',
|
||||
b"ffffffff",
|
||||
b"IBBDAT00",
|
||||
b"1",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"e474c1b",
|
||||
b"0",
|
||||
b"0",
|
||||
b"ff",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"0.000000",
|
||||
b"",
|
||||
b"",
|
||||
b"",
|
||||
b"\x96\xa2\x90\xdd\x92\xe8",
|
||||
b"\x8d\x81\x8d`",
|
||||
b"",
|
||||
b"",
|
||||
b"",
|
||||
]
|
||||
|
||||
if msg_type == 'new':
|
||||
if msg_type == "new":
|
||||
# New profile gets blank name, because we save over it at the end of the round.
|
||||
profiledata[27] = b''
|
||||
elif msg_type == 'existing':
|
||||
profiledata[27] = b""
|
||||
elif msg_type == "existing":
|
||||
# Exiting profile gets our hardcoded name saved.
|
||||
profiledata[27] = self.NAME.encode('shift-jis')
|
||||
profiledata[27] = self.NAME.encode("shift-jis")
|
||||
|
||||
# Construct node
|
||||
playerdata = Node.void('playerdata')
|
||||
playerdata = Node.void("playerdata")
|
||||
call.add_child(playerdata)
|
||||
playerdata.set_attribute('method', 'usergamedata_send')
|
||||
playerdata.add_child(Node.u32('retrycnt', 0))
|
||||
playerdata.set_attribute("method", "usergamedata_send")
|
||||
playerdata.add_child(Node.u32("retrycnt", 0))
|
||||
|
||||
data = Node.void('data')
|
||||
data = Node.void("data")
|
||||
playerdata.add_child(data)
|
||||
data.add_child(Node.string('eaid', ref_id))
|
||||
data.add_child(Node.string('gamekind', 'IBB'))
|
||||
data.add_child(Node.u32('datanum', 1))
|
||||
record = Node.void('record')
|
||||
data.add_child(Node.string("eaid", ref_id))
|
||||
data.add_child(Node.string("gamekind", "IBB"))
|
||||
data.add_child(Node.u32("datanum", 1))
|
||||
record = Node.void("record")
|
||||
data.add_child(record)
|
||||
d = Node.string('d', base64.b64encode(b','.join(profiledata)).decode('ascii'))
|
||||
d = Node.string("d", base64.b64encode(b",".join(profiledata)).decode("ascii"))
|
||||
record.add_child(d)
|
||||
d.add_child(Node.string('bin1', ''))
|
||||
d.add_child(Node.string("bin1", ""))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
self.assert_path(resp, "response/playerdata/result")
|
||||
|
||||
def verify_usergamedata_recv(self, ref_id: str) -> str:
|
||||
call = self.call_node()
|
||||
|
||||
# Construct node
|
||||
playerdata = Node.void('playerdata')
|
||||
playerdata = Node.void("playerdata")
|
||||
call.add_child(playerdata)
|
||||
playerdata.set_attribute('method', 'usergamedata_recv')
|
||||
data = Node.void('data')
|
||||
playerdata.set_attribute("method", "usergamedata_recv")
|
||||
data = Node.void("data")
|
||||
playerdata.add_child(data)
|
||||
data.add_child(Node.string('eaid', ref_id))
|
||||
data.add_child(Node.string('gamekind', 'IBB'))
|
||||
data.add_child(Node.u32('recv_num', 1))
|
||||
data.add_child(Node.string('recv_csv', 'IBBDAT00,3fffffffff'))
|
||||
data.add_child(Node.string("eaid", ref_id))
|
||||
data.add_child(Node.string("gamekind", "IBB"))
|
||||
data.add_child(Node.u32("recv_num", 1))
|
||||
data.add_child(Node.string("recv_csv", "IBBDAT00,3fffffffff"))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
self.assert_path(resp, "response/playerdata/result")
|
||||
self.assert_path(resp, "response/playerdata/player/record/d/bin1")
|
||||
self.assert_path(resp, "response/playerdata/player/record_num")
|
||||
|
||||
# Grab binary data, parse out name
|
||||
bindata = resp.child_value('playerdata/player/record/d')
|
||||
profiledata = base64.b64decode(bindata).split(b',')
|
||||
bindata = resp.child_value("playerdata/player/record/d")
|
||||
profiledata = base64.b64decode(bindata).split(b",")
|
||||
|
||||
# We lob off the first two values in returning profile, so the name is offset by two
|
||||
return profiledata[25].decode('shift-jis')
|
||||
return profiledata[25].decode("shift-jis")
|
||||
|
||||
def verify(self, cardid: Optional[str]) -> None:
|
||||
# Verify boot sequence is okay
|
||||
self.verify_services_get(
|
||||
expected_services=[
|
||||
'pcbtracker',
|
||||
'pcbevent',
|
||||
'local',
|
||||
'message',
|
||||
'facility',
|
||||
'cardmng',
|
||||
'package',
|
||||
'posevent',
|
||||
'pkglist',
|
||||
'dlstatus',
|
||||
'eacoin',
|
||||
'lobby',
|
||||
'ntp',
|
||||
'keepalive'
|
||||
"pcbtracker",
|
||||
"pcbevent",
|
||||
"local",
|
||||
"message",
|
||||
"facility",
|
||||
"cardmng",
|
||||
"package",
|
||||
"posevent",
|
||||
"pkglist",
|
||||
"dlstatus",
|
||||
"eacoin",
|
||||
"lobby",
|
||||
"ntp",
|
||||
"keepalive",
|
||||
]
|
||||
)
|
||||
paseli_enabled = self.verify_pcbtracker_alive()
|
||||
@ -190,33 +190,43 @@ class TheStarBishiBashiClient(BaseClient):
|
||||
print(f"Generated random card ID {card} for use.")
|
||||
|
||||
if cardid is None:
|
||||
self.verify_cardmng_inquire(card, msg_type='unregistered', paseli_enabled=paseli_enabled)
|
||||
self.verify_cardmng_inquire(
|
||||
card, msg_type="unregistered", paseli_enabled=paseli_enabled
|
||||
)
|
||||
ref_id = self.verify_cardmng_getrefid(card)
|
||||
if len(ref_id) != 16:
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when registering card')
|
||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='new', paseli_enabled=paseli_enabled):
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
||||
raise Exception(
|
||||
f"Invalid refid '{ref_id}' returned when registering card"
|
||||
)
|
||||
if ref_id != self.verify_cardmng_inquire(
|
||||
card, msg_type="new", paseli_enabled=paseli_enabled
|
||||
):
|
||||
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
|
||||
# Bishi doesn't read a new profile, it just writes out CSV for a blank one
|
||||
self.verify_usergamedata_send(ref_id, msg_type='new')
|
||||
self.verify_usergamedata_send(ref_id, msg_type="new")
|
||||
else:
|
||||
print("Skipping new card checks for existing card")
|
||||
ref_id = self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled)
|
||||
ref_id = self.verify_cardmng_inquire(
|
||||
card, msg_type="query", paseli_enabled=paseli_enabled
|
||||
)
|
||||
|
||||
# Verify pin handling and return card handling
|
||||
self.verify_cardmng_authpass(ref_id, correct=True)
|
||||
self.verify_cardmng_authpass(ref_id, correct=False)
|
||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled):
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
||||
if ref_id != self.verify_cardmng_inquire(
|
||||
card, msg_type="query", paseli_enabled=paseli_enabled
|
||||
):
|
||||
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
|
||||
|
||||
if cardid is None:
|
||||
# Verify profile saving
|
||||
name = self.verify_usergamedata_recv(ref_id)
|
||||
if name != '':
|
||||
raise Exception('New profile has a name associated with it!')
|
||||
if name != "":
|
||||
raise Exception("New profile has a name associated with it!")
|
||||
|
||||
self.verify_usergamedata_send(ref_id, msg_type='existing')
|
||||
self.verify_usergamedata_send(ref_id, msg_type="existing")
|
||||
name = self.verify_usergamedata_recv(ref_id)
|
||||
if name != self.NAME:
|
||||
raise Exception('Existing profile has no name associated with it!')
|
||||
raise Exception("Existing profile has no name associated with it!")
|
||||
else:
|
||||
print("Skipping score checks for existing card")
|
||||
|
@ -1,9 +1,9 @@
|
||||
import random
|
||||
|
||||
|
||||
def random_hex_string(length: int, caps: bool=False) -> str:
|
||||
def random_hex_string(length: int, caps: bool = False) -> str:
|
||||
if caps:
|
||||
string = '0123456789ABCDEF'
|
||||
string = "0123456789ABCDEF"
|
||||
else:
|
||||
string = '0123456789abcdef'
|
||||
return ''.join([random.choice(string) for x in range(length)])
|
||||
string = "0123456789abcdef"
|
||||
return "".join([random.choice(string) for x in range(length)])
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,54 +7,54 @@ from bemani.protocol import Node
|
||||
|
||||
|
||||
class DDR2014Client(BaseClient):
|
||||
NAME = 'TEST'
|
||||
NAME = "TEST"
|
||||
|
||||
def verify_game_shop(self, loc: str) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'shop')
|
||||
game.set_attribute('area', '51')
|
||||
game.set_attribute('boot', '34')
|
||||
game.set_attribute('close', '0')
|
||||
game.set_attribute('close_t', '0')
|
||||
game.set_attribute('coin', '02.01.--.--.01.G')
|
||||
game.set_attribute('diff', '3')
|
||||
game.set_attribute('during', '1')
|
||||
game.set_attribute('edit_cnt', '0')
|
||||
game.set_attribute('edit_used', '1')
|
||||
game.set_attribute('first', '1')
|
||||
game.set_attribute('ip', '1.5.7.3')
|
||||
game.set_attribute('is_freefirstplay', '1')
|
||||
game.set_attribute('is_paseli', '1')
|
||||
game.set_attribute('loc', loc)
|
||||
game.set_attribute('mac', '00:11:22:33:44:55')
|
||||
game.set_attribute('machine', '2')
|
||||
game.set_attribute('name', 'TEST')
|
||||
game.set_attribute('pay', '0')
|
||||
game.set_attribute('region', '.')
|
||||
game.set_attribute('soft', self.config['model'])
|
||||
game.set_attribute('softid', self.pcbid)
|
||||
game.set_attribute('stage', '1')
|
||||
game.set_attribute('time', '60')
|
||||
game.set_attribute('type', '0')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "shop")
|
||||
game.set_attribute("area", "51")
|
||||
game.set_attribute("boot", "34")
|
||||
game.set_attribute("close", "0")
|
||||
game.set_attribute("close_t", "0")
|
||||
game.set_attribute("coin", "02.01.--.--.01.G")
|
||||
game.set_attribute("diff", "3")
|
||||
game.set_attribute("during", "1")
|
||||
game.set_attribute("edit_cnt", "0")
|
||||
game.set_attribute("edit_used", "1")
|
||||
game.set_attribute("first", "1")
|
||||
game.set_attribute("ip", "1.5.7.3")
|
||||
game.set_attribute("is_freefirstplay", "1")
|
||||
game.set_attribute("is_paseli", "1")
|
||||
game.set_attribute("loc", loc)
|
||||
game.set_attribute("mac", "00:11:22:33:44:55")
|
||||
game.set_attribute("machine", "2")
|
||||
game.set_attribute("name", "TEST")
|
||||
game.set_attribute("pay", "0")
|
||||
game.set_attribute("region", ".")
|
||||
game.set_attribute("soft", self.config["model"])
|
||||
game.set_attribute("softid", self.pcbid)
|
||||
game.set_attribute("stage", "1")
|
||||
game.set_attribute("time", "60")
|
||||
game.set_attribute("type", "0")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game/@stop")
|
||||
|
||||
def verify_game_common(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'common')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "common")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game/flag/@id")
|
||||
@ -67,159 +67,159 @@ class DDR2014Client(BaseClient):
|
||||
|
||||
def verify_game_hiscore(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'hiscore')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "hiscore")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
for child in resp.child('game').children:
|
||||
self.assert_path(child, 'music/@reclink_num')
|
||||
self.assert_path(child, 'music/type/@diff')
|
||||
self.assert_path(child, 'music/type/name')
|
||||
self.assert_path(child, 'music/type/score')
|
||||
self.assert_path(child, 'music/type/area')
|
||||
self.assert_path(child, 'music/type/rank')
|
||||
self.assert_path(child, 'music/type/combo_type')
|
||||
self.assert_path(child, 'music/type/code')
|
||||
for child in resp.child("game").children:
|
||||
self.assert_path(child, "music/@reclink_num")
|
||||
self.assert_path(child, "music/type/@diff")
|
||||
self.assert_path(child, "music/type/name")
|
||||
self.assert_path(child, "music/type/score")
|
||||
self.assert_path(child, "music/type/area")
|
||||
self.assert_path(child, "music/type/rank")
|
||||
self.assert_path(child, "music/type/combo_type")
|
||||
self.assert_path(child, "music/type/code")
|
||||
|
||||
def verify_game_area_hiscore(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'area_hiscore')
|
||||
game.set_attribute('shop_area', '51')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "area_hiscore")
|
||||
game.set_attribute("shop_area", "51")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
for child in resp.child('game').children:
|
||||
self.assert_path(child, 'music/@reclink_num')
|
||||
self.assert_path(child, 'music/type/@diff')
|
||||
self.assert_path(child, 'music/type/name')
|
||||
self.assert_path(child, 'music/type/score')
|
||||
self.assert_path(child, 'music/type/area')
|
||||
self.assert_path(child, 'music/type/rank')
|
||||
self.assert_path(child, 'music/type/combo_type')
|
||||
self.assert_path(child, 'music/type/code')
|
||||
for child in resp.child("game").children:
|
||||
self.assert_path(child, "music/@reclink_num")
|
||||
self.assert_path(child, "music/type/@diff")
|
||||
self.assert_path(child, "music/type/name")
|
||||
self.assert_path(child, "music/type/score")
|
||||
self.assert_path(child, "music/type/area")
|
||||
self.assert_path(child, "music/type/rank")
|
||||
self.assert_path(child, "music/type/combo_type")
|
||||
self.assert_path(child, "music/type/code")
|
||||
|
||||
def verify_game_message(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'message')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "message")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_ranking(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'ranking')
|
||||
game.set_attribute('max', '10')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "ranking")
|
||||
game.set_attribute("max", "10")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_log(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'log')
|
||||
game.set_attribute('type', '0')
|
||||
game.set_attribute('soft', self.config['model'])
|
||||
game.set_attribute('softid', self.pcbid)
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('boot', '34')
|
||||
game.set_attribute('mac', '00:11:22:33:44:55')
|
||||
clear = Node.void('clear')
|
||||
game.set_attribute("method", "log")
|
||||
game.set_attribute("type", "0")
|
||||
game.set_attribute("soft", self.config["model"])
|
||||
game.set_attribute("softid", self.pcbid)
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("boot", "34")
|
||||
game.set_attribute("mac", "00:11:22:33:44:55")
|
||||
clear = Node.void("clear")
|
||||
game.add_child(clear)
|
||||
clear.set_attribute('book', '0')
|
||||
clear.set_attribute('edit', '0')
|
||||
clear.set_attribute('rank', '0')
|
||||
clear.set_attribute('set', '0')
|
||||
auto = Node.void('auto')
|
||||
clear.set_attribute("book", "0")
|
||||
clear.set_attribute("edit", "0")
|
||||
clear.set_attribute("rank", "0")
|
||||
clear.set_attribute("set", "0")
|
||||
auto = Node.void("auto")
|
||||
game.add_child(auto)
|
||||
auto.set_attribute('book', '1')
|
||||
auto.set_attribute('edit', '1')
|
||||
auto.set_attribute('rank', '1')
|
||||
auto.set_attribute('set', '1')
|
||||
auto.set_attribute("book", "1")
|
||||
auto.set_attribute("edit", "1")
|
||||
auto.set_attribute("rank", "1")
|
||||
auto.set_attribute("set", "1")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_tax_info(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'tax_info')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute("method", "tax_info")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game/tax_info/@tax_phase")
|
||||
|
||||
def verify_game_recorder(self) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'recorder')
|
||||
game.set_attribute('assert_cnt', '0')
|
||||
game.set_attribute('assert_info', '')
|
||||
game.set_attribute('assert_path', '')
|
||||
game.set_attribute('assert_time', '0')
|
||||
game.set_attribute('boot_time', '1706151228')
|
||||
game.set_attribute('cnt_demo', '1')
|
||||
game.set_attribute('cnt_music', '1')
|
||||
game.set_attribute('cnt_play', '0')
|
||||
game.set_attribute('last_mid', '481')
|
||||
game.set_attribute('last_seq', '36')
|
||||
game.set_attribute('last_step', '0')
|
||||
game.set_attribute('last_time', '1706151235')
|
||||
game.set_attribute('softcode', self.config['model'])
|
||||
game.set_attribute('temp_seq', '15')
|
||||
game.set_attribute('temp_step', '8')
|
||||
game.set_attribute('temp_time', '1706151234')
|
||||
game.set_attribute('wd_restart', '0')
|
||||
game.set_attribute("method", "recorder")
|
||||
game.set_attribute("assert_cnt", "0")
|
||||
game.set_attribute("assert_info", "")
|
||||
game.set_attribute("assert_path", "")
|
||||
game.set_attribute("assert_time", "0")
|
||||
game.set_attribute("boot_time", "1706151228")
|
||||
game.set_attribute("cnt_demo", "1")
|
||||
game.set_attribute("cnt_music", "1")
|
||||
game.set_attribute("cnt_play", "0")
|
||||
game.set_attribute("last_mid", "481")
|
||||
game.set_attribute("last_seq", "36")
|
||||
game.set_attribute("last_step", "0")
|
||||
game.set_attribute("last_time", "1706151235")
|
||||
game.set_attribute("softcode", self.config["model"])
|
||||
game.set_attribute("temp_seq", "15")
|
||||
game.set_attribute("temp_step", "8")
|
||||
game.set_attribute("temp_time", "1706151234")
|
||||
game.set_attribute("wd_restart", "0")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_lock(self, ref_id: str, play: int) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute('method', 'lock')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('play', str(play))
|
||||
game.set_attribute("refid", ref_id)
|
||||
game.set_attribute("method", "lock")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("play", str(play))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game/@now_login")
|
||||
@ -228,34 +228,34 @@ class DDR2014Client(BaseClient):
|
||||
# Pad the name to 8 characters
|
||||
name = self.NAME[:8]
|
||||
while len(name) < 8:
|
||||
name = name + ' '
|
||||
name = name + " "
|
||||
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'new')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('name', name)
|
||||
game.set_attribute('area', '51')
|
||||
game.set_attribute('old', '0')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute("method", "new")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("name", name)
|
||||
game.set_attribute("area", "51")
|
||||
game.set_attribute("old", "0")
|
||||
game.set_attribute("refid", ref_id)
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_load_daily(self, ref_id: str) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'load_daily')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute("method", "load_daily")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("refid", ref_id)
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
self.assert_path(resp, "response/game/daycount/@playcount")
|
||||
self.assert_path(resp, "response/game/dailycombo/@daily_combo")
|
||||
@ -263,20 +263,20 @@ class DDR2014Client(BaseClient):
|
||||
|
||||
def verify_game_load(self, ref_id: str, msg_type: str) -> Dict[str, Any]:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'load')
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute("method", "load")
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("refid", ref_id)
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
if msg_type == 'new':
|
||||
if msg_type == "new":
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game/@none")
|
||||
return {}
|
||||
if msg_type == 'existing':
|
||||
if msg_type == "existing":
|
||||
# Verify existing profile and return info
|
||||
self.assert_path(resp, "response/game/seq")
|
||||
self.assert_path(resp, "response/game/code")
|
||||
@ -345,63 +345,63 @@ class DDR2014Client(BaseClient):
|
||||
for i in range(55):
|
||||
self.assert_path(resp, f"response/game/play_area/@play_cnt{i}")
|
||||
|
||||
gr_s = resp.child('game/gr_s')
|
||||
gr_d = resp.child('game/gr_d')
|
||||
gr_s = resp.child("game/gr_s")
|
||||
gr_d = resp.child("game/gr_d")
|
||||
|
||||
return {
|
||||
'name': resp.child_value('game/name'),
|
||||
'ext_id': resp.child_value('game/code'),
|
||||
'single_plays': resp.child_value('game/cnt_s'),
|
||||
'double_plays': resp.child_value('game/cnt_d'),
|
||||
'groove_single': [
|
||||
int(gr_s.attribute('gr1')),
|
||||
int(gr_s.attribute('gr2')),
|
||||
int(gr_s.attribute('gr3')),
|
||||
int(gr_s.attribute('gr4')),
|
||||
int(gr_s.attribute('gr5')),
|
||||
"name": resp.child_value("game/name"),
|
||||
"ext_id": resp.child_value("game/code"),
|
||||
"single_plays": resp.child_value("game/cnt_s"),
|
||||
"double_plays": resp.child_value("game/cnt_d"),
|
||||
"groove_single": [
|
||||
int(gr_s.attribute("gr1")),
|
||||
int(gr_s.attribute("gr2")),
|
||||
int(gr_s.attribute("gr3")),
|
||||
int(gr_s.attribute("gr4")),
|
||||
int(gr_s.attribute("gr5")),
|
||||
],
|
||||
'groove_double': [
|
||||
int(gr_d.attribute('gr1')),
|
||||
int(gr_d.attribute('gr2')),
|
||||
int(gr_d.attribute('gr3')),
|
||||
int(gr_d.attribute('gr4')),
|
||||
int(gr_d.attribute('gr5')),
|
||||
"groove_double": [
|
||||
int(gr_d.attribute("gr1")),
|
||||
int(gr_d.attribute("gr2")),
|
||||
int(gr_d.attribute("gr3")),
|
||||
int(gr_d.attribute("gr4")),
|
||||
int(gr_d.attribute("gr5")),
|
||||
],
|
||||
}
|
||||
|
||||
raise Exception('Unknown load type!')
|
||||
raise Exception("Unknown load type!")
|
||||
|
||||
def verify_game_load_m(self, ref_id: str) -> Dict[int, Dict[int, Dict[str, Any]]]:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('all', '1')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute('method', 'load_m')
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("all", "1")
|
||||
game.set_attribute("refid", ref_id)
|
||||
game.set_attribute("method", "load_m")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
scores: Dict[int, Dict[int, Dict[str, Any]]] = {}
|
||||
self.assert_path(resp, "response/game")
|
||||
for child in resp.child('game').children:
|
||||
self.assert_path(child, 'music/@reclink')
|
||||
reclink = int(child.attribute('reclink'))
|
||||
for child in resp.child("game").children:
|
||||
self.assert_path(child, "music/@reclink")
|
||||
reclink = int(child.attribute("reclink"))
|
||||
|
||||
for typenode in child.children:
|
||||
self.assert_path(typenode, 'type/@diff')
|
||||
self.assert_path(typenode, 'type/score')
|
||||
self.assert_path(typenode, 'type/count')
|
||||
self.assert_path(typenode, 'type/rank')
|
||||
self.assert_path(typenode, 'type/combo_type')
|
||||
chart = int(typenode.attribute('diff'))
|
||||
self.assert_path(typenode, "type/@diff")
|
||||
self.assert_path(typenode, "type/score")
|
||||
self.assert_path(typenode, "type/count")
|
||||
self.assert_path(typenode, "type/rank")
|
||||
self.assert_path(typenode, "type/combo_type")
|
||||
chart = int(typenode.attribute("diff"))
|
||||
vals = {
|
||||
'score': typenode.child_value('score'),
|
||||
'count': typenode.child_value('count'),
|
||||
'rank': typenode.child_value('rank'),
|
||||
'halo': typenode.child_value('combo_type'),
|
||||
"score": typenode.child_value("score"),
|
||||
"count": typenode.child_value("count"),
|
||||
"rank": typenode.child_value("rank"),
|
||||
"halo": typenode.child_value("combo_type"),
|
||||
}
|
||||
if reclink not in scores:
|
||||
scores[reclink] = {}
|
||||
@ -410,91 +410,95 @@ class DDR2014Client(BaseClient):
|
||||
|
||||
def verify_game_load_edit(self, ref_id: str) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('pid', '0')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute('method', 'load_edit')
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("pid", "0")
|
||||
game.set_attribute("refid", ref_id)
|
||||
game.set_attribute("method", "load_edit")
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_save(self, ref_id: str, style: int, gauge: Optional[List[int]]=None) -> None:
|
||||
def verify_game_save(
|
||||
self, ref_id: str, style: int, gauge: Optional[List[int]] = None
|
||||
) -> None:
|
||||
gauge = gauge or [0, 0, 0, 0, 0]
|
||||
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'save')
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute('ver', '2014102700')
|
||||
game.set_attribute('shop_area', '51')
|
||||
last = Node.void('last')
|
||||
game.set_attribute("method", "save")
|
||||
game.set_attribute("refid", ref_id)
|
||||
game.set_attribute("ver", "2014102700")
|
||||
game.set_attribute("shop_area", "51")
|
||||
last = Node.void("last")
|
||||
game.add_child(last)
|
||||
last.set_attribute('mode', '1')
|
||||
last.set_attribute('style', str(style))
|
||||
gr = Node.void('gr')
|
||||
last.set_attribute("mode", "1")
|
||||
last.set_attribute("style", str(style))
|
||||
gr = Node.void("gr")
|
||||
game.add_child(gr)
|
||||
gr.set_attribute('gr1', str(gauge[0]))
|
||||
gr.set_attribute('gr2', str(gauge[1]))
|
||||
gr.set_attribute('gr3', str(gauge[2]))
|
||||
gr.set_attribute('gr4', str(gauge[3]))
|
||||
gr.set_attribute('gr5', str(gauge[4]))
|
||||
gr.set_attribute("gr1", str(gauge[0]))
|
||||
gr.set_attribute("gr2", str(gauge[1]))
|
||||
gr.set_attribute("gr3", str(gauge[2]))
|
||||
gr.set_attribute("gr4", str(gauge[3]))
|
||||
gr.set_attribute("gr5", str(gauge[4]))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
|
||||
def verify_game_save_m(self, ref_id: str, ext_id: str, score: Dict[str, Any]) -> None:
|
||||
def verify_game_save_m(
|
||||
self, ref_id: str, ext_id: str, score: Dict[str, Any]
|
||||
) -> None:
|
||||
call = self.call_node()
|
||||
game = Node.void('game')
|
||||
game = Node.void("game")
|
||||
call.add_child(game)
|
||||
game.set_attribute('method', 'save_m')
|
||||
game.set_attribute('diff', '12345')
|
||||
game.set_attribute('mtype', str(score['chart']))
|
||||
game.set_attribute('mid', str(score['id']))
|
||||
game.set_attribute('refid', ref_id)
|
||||
game.set_attribute('ver', '2014102700')
|
||||
data = Node.void('data')
|
||||
game.set_attribute("method", "save_m")
|
||||
game.set_attribute("diff", "12345")
|
||||
game.set_attribute("mtype", str(score["chart"]))
|
||||
game.set_attribute("mid", str(score["id"]))
|
||||
game.set_attribute("refid", ref_id)
|
||||
game.set_attribute("ver", "2014102700")
|
||||
data = Node.void("data")
|
||||
game.add_child(data)
|
||||
data.set_attribute('score', str(score['score']))
|
||||
data.set_attribute('rank', str(score['rank']))
|
||||
data.set_attribute('shop_area', '0')
|
||||
data.set_attribute('playmode', '1')
|
||||
data.set_attribute('combo', str(score['combo']))
|
||||
data.set_attribute('phase', '1')
|
||||
data.set_attribute('style', '0')
|
||||
data.set_attribute('full', '1' if score['halo'] >= 1 else '0')
|
||||
data.set_attribute('great_fc', '1' if score['halo'] == 1 else '0')
|
||||
data.set_attribute('good_fc', '1' if score['halo'] == 4 else '0')
|
||||
data.set_attribute('perf_fc', '1' if score['halo'] == 2 else '0')
|
||||
gauge = Node.void('gauge')
|
||||
data.set_attribute("score", str(score["score"]))
|
||||
data.set_attribute("rank", str(score["rank"]))
|
||||
data.set_attribute("shop_area", "0")
|
||||
data.set_attribute("playmode", "1")
|
||||
data.set_attribute("combo", str(score["combo"]))
|
||||
data.set_attribute("phase", "1")
|
||||
data.set_attribute("style", "0")
|
||||
data.set_attribute("full", "1" if score["halo"] >= 1 else "0")
|
||||
data.set_attribute("great_fc", "1" if score["halo"] == 1 else "0")
|
||||
data.set_attribute("good_fc", "1" if score["halo"] == 4 else "0")
|
||||
data.set_attribute("perf_fc", "1" if score["halo"] == 2 else "0")
|
||||
gauge = Node.void("gauge")
|
||||
game.add_child(gauge)
|
||||
gauge.set_attribute('life8', '0')
|
||||
gauge.set_attribute('assist', '0')
|
||||
gauge.set_attribute('risky', '0')
|
||||
gauge.set_attribute('life4', '0')
|
||||
gauge.set_attribute('hard', '0')
|
||||
player = Node.void('player')
|
||||
gauge.set_attribute("life8", "0")
|
||||
gauge.set_attribute("assist", "0")
|
||||
gauge.set_attribute("risky", "0")
|
||||
gauge.set_attribute("life4", "0")
|
||||
gauge.set_attribute("hard", "0")
|
||||
player = Node.void("player")
|
||||
game.add_child(player)
|
||||
player.set_attribute('playcnt', '123')
|
||||
player.set_attribute('code', ext_id)
|
||||
option = Node.void('option_02')
|
||||
player.set_attribute("playcnt", "123")
|
||||
player.set_attribute("code", ext_id)
|
||||
option = Node.void("option_02")
|
||||
game.add_child(option)
|
||||
option.set_attribute('opt02_0', '6')
|
||||
option.set_attribute('opt02_6', '1')
|
||||
option.set_attribute('opt02_13', '2')
|
||||
game.add_child(Node.u8_array('trace', [0] * 512))
|
||||
game.add_child(Node.u32('size', 512))
|
||||
option.set_attribute("opt02_0", "6")
|
||||
option.set_attribute("opt02_6", "1")
|
||||
option.set_attribute("opt02_13", "2")
|
||||
game.add_child(Node.u8_array("trace", [0] * 512))
|
||||
game.add_child(Node.u32("size", 512))
|
||||
|
||||
# Swap with server
|
||||
resp = self.exchange('', call)
|
||||
resp = self.exchange("", call)
|
||||
|
||||
# Verify that response is correct
|
||||
self.assert_path(resp, "response/game")
|
||||
@ -503,26 +507,26 @@ class DDR2014Client(BaseClient):
|
||||
# Verify boot sequence is okay
|
||||
self.verify_services_get(
|
||||
expected_services=[
|
||||
'pcbtracker',
|
||||
'pcbevent',
|
||||
'local',
|
||||
'message',
|
||||
'facility',
|
||||
'cardmng',
|
||||
'package',
|
||||
'posevent',
|
||||
'pkglist',
|
||||
'dlstatus',
|
||||
'eacoin',
|
||||
'lobby',
|
||||
'ntp',
|
||||
'keepalive'
|
||||
"pcbtracker",
|
||||
"pcbevent",
|
||||
"local",
|
||||
"message",
|
||||
"facility",
|
||||
"cardmng",
|
||||
"package",
|
||||
"posevent",
|
||||
"pkglist",
|
||||
"dlstatus",
|
||||
"eacoin",
|
||||
"lobby",
|
||||
"ntp",
|
||||
"keepalive",
|
||||
]
|
||||
)
|
||||
paseli_enabled = self.verify_pcbtracker_alive()
|
||||
self.verify_message_get()
|
||||
self.verify_package_list()
|
||||
location = self.verify_facility_get('EUC_JP')
|
||||
location = self.verify_facility_get("EUC_JP")
|
||||
self.verify_pcbevent_put()
|
||||
self.verify_game_recorder()
|
||||
self.verify_game_tax_info()
|
||||
@ -542,24 +546,34 @@ class DDR2014Client(BaseClient):
|
||||
print(f"Generated random card ID {card} for use.")
|
||||
|
||||
if cardid is None:
|
||||
self.verify_cardmng_inquire(card, msg_type='unregistered', paseli_enabled=paseli_enabled)
|
||||
self.verify_cardmng_inquire(
|
||||
card, msg_type="unregistered", paseli_enabled=paseli_enabled
|
||||
)
|
||||
ref_id = self.verify_cardmng_getrefid(card)
|
||||
if len(ref_id) != 16:
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when registering card')
|
||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='new', paseli_enabled=paseli_enabled):
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
||||
raise Exception(
|
||||
f"Invalid refid '{ref_id}' returned when registering card"
|
||||
)
|
||||
if ref_id != self.verify_cardmng_inquire(
|
||||
card, msg_type="new", paseli_enabled=paseli_enabled
|
||||
):
|
||||
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
|
||||
# Bishi doesn't read a new profile, it just writes out CSV for a blank one
|
||||
self.verify_game_load(ref_id, msg_type='new')
|
||||
self.verify_game_load(ref_id, msg_type="new")
|
||||
self.verify_game_new(ref_id)
|
||||
else:
|
||||
print("Skipping new card checks for existing card")
|
||||
ref_id = self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled)
|
||||
ref_id = self.verify_cardmng_inquire(
|
||||
card, msg_type="query", paseli_enabled=paseli_enabled
|
||||
)
|
||||
|
||||
# Verify pin handling and return card handling
|
||||
self.verify_cardmng_authpass(ref_id, correct=True)
|
||||
self.verify_cardmng_authpass(ref_id, correct=False)
|
||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled):
|
||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
||||
if ref_id != self.verify_cardmng_inquire(
|
||||
card, msg_type="query", paseli_enabled=paseli_enabled
|
||||
):
|
||||
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
|
||||
|
||||
# Verify locking and unlocking profile ability
|
||||
self.verify_game_lock(ref_id, 1)
|
||||
@ -567,53 +581,53 @@ class DDR2014Client(BaseClient):
|
||||
|
||||
if cardid is None:
|
||||
# Verify empty profile
|
||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
||||
ext_id = str(profile['ext_id'])
|
||||
if profile['name'] != self.NAME:
|
||||
raise Exception('Profile has invalid name associated with it!')
|
||||
if profile['single_plays'] != 0:
|
||||
raise Exception('Profile has plays on single already!')
|
||||
if profile['double_plays'] != 0:
|
||||
raise Exception('Profile has plays on double already!')
|
||||
if any([g != 0 for g in profile['groove_single']]):
|
||||
raise Exception('Profile has single groove gauge values already!')
|
||||
if any([g != 0 for g in profile['groove_double']]):
|
||||
raise Exception('Profile has double groove gauge values already!')
|
||||
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||
ext_id = str(profile["ext_id"])
|
||||
if profile["name"] != self.NAME:
|
||||
raise Exception("Profile has invalid name associated with it!")
|
||||
if profile["single_plays"] != 0:
|
||||
raise Exception("Profile has plays on single already!")
|
||||
if profile["double_plays"] != 0:
|
||||
raise Exception("Profile has plays on double already!")
|
||||
if any([g != 0 for g in profile["groove_single"]]):
|
||||
raise Exception("Profile has single groove gauge values already!")
|
||||
if any([g != 0 for g in profile["groove_double"]]):
|
||||
raise Exception("Profile has double groove gauge values already!")
|
||||
|
||||
# Verify empty scores
|
||||
scores = self.verify_game_load_m(ref_id)
|
||||
if len(scores) > 0:
|
||||
raise Exception('Scores exist on new profile!')
|
||||
raise Exception("Scores exist on new profile!")
|
||||
|
||||
self.verify_game_load_edit(ref_id)
|
||||
self.verify_game_load_daily(ref_id)
|
||||
|
||||
# Verify profile saving
|
||||
self.verify_game_save(ref_id, 0, [1, 2, 3, 4, 5])
|
||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
||||
if profile['name'] != self.NAME:
|
||||
raise Exception('Profile has invalid name associated with it!')
|
||||
if profile['single_plays'] != 1:
|
||||
raise Exception('Profile has invalid plays on single!')
|
||||
if profile['double_plays'] != 0:
|
||||
raise Exception('Profile has invalid plays on double!')
|
||||
if profile['groove_single'] != [1, 2, 3, 4, 5]:
|
||||
raise Exception('Profile has invalid single groove gauge values!')
|
||||
if any([g != 0 for g in profile['groove_double']]):
|
||||
raise Exception('Profile has invalid double groove gauge values!')
|
||||
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||
if profile["name"] != self.NAME:
|
||||
raise Exception("Profile has invalid name associated with it!")
|
||||
if profile["single_plays"] != 1:
|
||||
raise Exception("Profile has invalid plays on single!")
|
||||
if profile["double_plays"] != 0:
|
||||
raise Exception("Profile has invalid plays on double!")
|
||||
if profile["groove_single"] != [1, 2, 3, 4, 5]:
|
||||
raise Exception("Profile has invalid single groove gauge values!")
|
||||
if any([g != 0 for g in profile["groove_double"]]):
|
||||
raise Exception("Profile has invalid double groove gauge values!")
|
||||
|
||||
self.verify_game_save(ref_id, 1, [5, 4, 3, 2, 1])
|
||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
||||
if profile['name'] != self.NAME:
|
||||
raise Exception('Profile has invalid name associated with it!')
|
||||
if profile['single_plays'] != 1:
|
||||
raise Exception('Profile has invalid plays on single!')
|
||||
if profile['double_plays'] != 1:
|
||||
raise Exception('Profile has invalid plays on double!')
|
||||
if profile['groove_single'] != [1, 2, 3, 4, 5]:
|
||||
raise Exception('Profile has invalid single groove gauge values!')
|
||||
if profile['groove_double'] != [5, 4, 3, 2, 1]:
|
||||
raise Exception('Profile has invalid double groove gauge values!')
|
||||
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||
if profile["name"] != self.NAME:
|
||||
raise Exception("Profile has invalid name associated with it!")
|
||||
if profile["single_plays"] != 1:
|
||||
raise Exception("Profile has invalid plays on single!")
|
||||
if profile["double_plays"] != 1:
|
||||
raise Exception("Profile has invalid plays on double!")
|
||||
if profile["groove_single"] != [1, 2, 3, 4, 5]:
|
||||
raise Exception("Profile has invalid single groove gauge values!")
|
||||
if profile["groove_double"] != [5, 4, 3, 2, 1]:
|
||||
raise Exception("Profile has invalid double groove gauge values!")
|
||||
|
||||
# Now, write some scores and verify saving
|
||||
for phase in [1, 2]:
|
||||
@ -621,71 +635,71 @@ class DDR2014Client(BaseClient):
|
||||
dummyscores = [
|
||||
# An okay score on a chart
|
||||
{
|
||||
'id': 593,
|
||||
'chart': 3,
|
||||
'score': 800000,
|
||||
'combo': 123,
|
||||
'rank': 4,
|
||||
'halo': 1,
|
||||
"id": 593,
|
||||
"chart": 3,
|
||||
"score": 800000,
|
||||
"combo": 123,
|
||||
"rank": 4,
|
||||
"halo": 1,
|
||||
},
|
||||
# A good score on an easier chart same song
|
||||
{
|
||||
'id': 593,
|
||||
'chart': 2,
|
||||
'score': 990000,
|
||||
'combo': 321,
|
||||
'rank': 2,
|
||||
'halo': 2,
|
||||
"id": 593,
|
||||
"chart": 2,
|
||||
"score": 990000,
|
||||
"combo": 321,
|
||||
"rank": 2,
|
||||
"halo": 2,
|
||||
},
|
||||
# A perfect score
|
||||
{
|
||||
'id': 483,
|
||||
'chart': 3,
|
||||
'score': 1000000,
|
||||
'combo': 400,
|
||||
'rank': 1,
|
||||
'halo': 3,
|
||||
"id": 483,
|
||||
"chart": 3,
|
||||
"score": 1000000,
|
||||
"combo": 400,
|
||||
"rank": 1,
|
||||
"halo": 3,
|
||||
},
|
||||
# A bad score
|
||||
{
|
||||
'id': 483,
|
||||
'chart': 2,
|
||||
'score': 100000,
|
||||
'combo': 5,
|
||||
'rank': 7,
|
||||
'halo': 0,
|
||||
"id": 483,
|
||||
"chart": 2,
|
||||
"score": 100000,
|
||||
"combo": 5,
|
||||
"rank": 7,
|
||||
"halo": 0,
|
||||
},
|
||||
{
|
||||
'id': 483,
|
||||
'chart': 1,
|
||||
'score': 60000,
|
||||
'combo': 5,
|
||||
'rank': 6,
|
||||
'halo': 4,
|
||||
"id": 483,
|
||||
"chart": 1,
|
||||
"score": 60000,
|
||||
"combo": 5,
|
||||
"rank": 6,
|
||||
"halo": 4,
|
||||
},
|
||||
]
|
||||
if phase == 2:
|
||||
dummyscores = [
|
||||
# A better score on a chart
|
||||
{
|
||||
'id': 593,
|
||||
'chart': 3,
|
||||
'score': 850000,
|
||||
'combo': 234,
|
||||
'rank': 3,
|
||||
'halo': 2,
|
||||
"id": 593,
|
||||
"chart": 3,
|
||||
"score": 850000,
|
||||
"combo": 234,
|
||||
"rank": 3,
|
||||
"halo": 2,
|
||||
},
|
||||
# A worse score on another chart
|
||||
{
|
||||
'id': 593,
|
||||
'chart': 2,
|
||||
'score': 980000,
|
||||
'combo': 300,
|
||||
'rank': 3,
|
||||
'halo': 0,
|
||||
'expected_score': 990000,
|
||||
'expected_rank': 2,
|
||||
'expected_halo': 2,
|
||||
"id": 593,
|
||||
"chart": 2,
|
||||
"score": 980000,
|
||||
"combo": 300,
|
||||
"rank": 3,
|
||||
"halo": 0,
|
||||
"expected_score": 990000,
|
||||
"expected_rank": 2,
|
||||
"expected_halo": 2,
|
||||
},
|
||||
]
|
||||
|
||||
@ -693,21 +707,29 @@ class DDR2014Client(BaseClient):
|
||||
self.verify_game_save_m(ref_id, ext_id, score)
|
||||
scores = self.verify_game_load_m(ref_id)
|
||||
for score in dummyscores:
|
||||
data = scores.get(score['id'], {}).get(score['chart'], None)
|
||||
data = scores.get(score["id"], {}).get(score["chart"], None)
|
||||
if data is None:
|
||||
raise Exception(f'Expected to get score back for song {score["id"]} chart {score["chart"]}!')
|
||||
raise Exception(
|
||||
f'Expected to get score back for song {score["id"]} chart {score["chart"]}!'
|
||||
)
|
||||
|
||||
# Verify the attributes of the score
|
||||
expected_score = score.get('expected_score', score['score'])
|
||||
expected_rank = score.get('expected_rank', score['rank'])
|
||||
expected_halo = score.get('expected_halo', score['halo'])
|
||||
expected_score = score.get("expected_score", score["score"])
|
||||
expected_rank = score.get("expected_rank", score["rank"])
|
||||
expected_halo = score.get("expected_halo", score["halo"])
|
||||
|
||||
if data['score'] != expected_score:
|
||||
raise Exception(f'Expected a score of \'{expected_score}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got score \'{data["score"]}\'')
|
||||
if data['rank'] != expected_rank:
|
||||
raise Exception(f'Expected a rank of \'{expected_rank}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got rank \'{data["rank"]}\'')
|
||||
if data['halo'] != expected_halo:
|
||||
raise Exception(f'Expected a halo of \'{expected_halo}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got halo \'{data["halo"]}\'')
|
||||
if data["score"] != expected_score:
|
||||
raise Exception(
|
||||
f'Expected a score of \'{expected_score}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got score \'{data["score"]}\''
|
||||
)
|
||||
if data["rank"] != expected_rank:
|
||||
raise Exception(
|
||||
f'Expected a rank of \'{expected_rank}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got rank \'{data["rank"]}\''
|
||||
)
|
||||
if data["halo"] != expected_halo:
|
||||
raise Exception(
|
||||
f'Expected a halo of \'{expected_halo}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got halo \'{data["halo"]}\''
|
||||
)
|
||||
|
||||
# Sleep so we don't end up putting in score history on the same second
|
||||
time.sleep(1)
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user