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
|
`./dbutils --help` to see all options. The config file that this works on is the same
|
||||||
that is given to "api", "services" and "frontend".
|
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
|
## frontend
|
||||||
|
|
||||||
Development version of a frontend server allowing for account and server administration
|
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
|
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
|
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`.
|
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
|
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
|
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
|
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 functools import wraps
|
||||||
|
|
||||||
from bemani.api.exceptions import APIException
|
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.api.types import g
|
||||||
from bemani.common import GameConstants, APIConstants, VersionConstants
|
from bemani.common import GameConstants, APIConstants, VersionConstants
|
||||||
from bemani.data import Config, Data
|
from bemani.data import Config, Data
|
||||||
|
|
||||||
app = Flask(
|
app = Flask(__name__)
|
||||||
__name__
|
|
||||||
)
|
|
||||||
config = Config()
|
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(
|
return Response(
|
||||||
json.dumps(data).encode('utf8'),
|
json.dumps(data).encode("utf8"),
|
||||||
content_type="application/json; charset=utf-8",
|
content_type="application/json; charset=utf-8",
|
||||||
status=code,
|
status=code,
|
||||||
)
|
)
|
||||||
@ -35,15 +38,15 @@ def before_request() -> None:
|
|||||||
g.data = Data(config)
|
g.data = Data(config)
|
||||||
g.authorized = False
|
g.authorized = False
|
||||||
|
|
||||||
authkey = request.headers.get('Authorization')
|
authkey = request.headers.get("Authorization")
|
||||||
if authkey is not None:
|
if authkey is not None:
|
||||||
try:
|
try:
|
||||||
authtype, authtoken = authkey.split(' ', 1)
|
authtype, authtoken = authkey.split(" ", 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
authtype = None
|
authtype = None
|
||||||
authtoken = None
|
authtoken = None
|
||||||
|
|
||||||
if authtype.lower() == 'token':
|
if authtype.lower() == "token":
|
||||||
g.authorized = g.data.local.api.validate_client(authtoken)
|
g.authorized = g.data.local.api.validate_client(authtoken)
|
||||||
|
|
||||||
|
|
||||||
@ -59,7 +62,7 @@ def after_request(response: Response) -> Response:
|
|||||||
|
|
||||||
@app.teardown_request
|
@app.teardown_request
|
||||||
def teardown_request(exception: Any) -> None:
|
def teardown_request(exception: Any) -> None:
|
||||||
data = getattr(g, 'data', None)
|
data = getattr(g, "data", None)
|
||||||
if data is not None:
|
if data is not None:
|
||||||
data.close()
|
data.close()
|
||||||
|
|
||||||
@ -69,11 +72,12 @@ def authrequired(func: Callable) -> Callable:
|
|||||||
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
||||||
if not g.authorized:
|
if not g.authorized:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Unauthorized client!'},
|
{"error": "Unauthorized client!"},
|
||||||
401,
|
401,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
return decoratedfunction
|
return decoratedfunction
|
||||||
|
|
||||||
|
|
||||||
@ -81,27 +85,30 @@ def jsonify(func: Callable) -> Callable:
|
|||||||
@wraps(func)
|
@wraps(func)
|
||||||
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
def decoratedfunction(*args: Any, **kwargs: Any) -> Response:
|
||||||
return jsonify_response(func(*args, **kwargs))
|
return jsonify_response(func(*args, **kwargs))
|
||||||
|
|
||||||
return decoratedfunction
|
return decoratedfunction
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(Exception)
|
@app.errorhandler(Exception)
|
||||||
def server_exception(exception: Any) -> Response:
|
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)
|
print(stack)
|
||||||
try:
|
try:
|
||||||
g.data.local.network.put_event(
|
g.data.local.network.put_event(
|
||||||
'exception',
|
"exception",
|
||||||
{
|
{
|
||||||
'service': 'api',
|
"service": "api",
|
||||||
'request': request.url,
|
"request": request.url,
|
||||||
'traceback': stack,
|
"traceback": stack,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Exception occured while processing request.'},
|
{"error": "Exception occured while processing request."},
|
||||||
500,
|
500,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -109,7 +116,7 @@ def server_exception(exception: Any) -> Response:
|
|||||||
@app.errorhandler(APIException)
|
@app.errorhandler(APIException)
|
||||||
def api_exception(exception: Any) -> Response:
|
def api_exception(exception: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': exception.message},
|
{"error": exception.message},
|
||||||
exception.code,
|
exception.code,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -117,7 +124,7 @@ def api_exception(exception: Any) -> Response:
|
|||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def server_error(error: Any) -> Response:
|
def server_error(error: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Exception occured while processing request.'},
|
{"error": "Exception occured while processing request."},
|
||||||
500,
|
500,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -125,7 +132,7 @@ def server_error(error: Any) -> Response:
|
|||||||
@app.errorhandler(501)
|
@app.errorhandler(501)
|
||||||
def protocol_error(error: Any) -> Response:
|
def protocol_error(error: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Unsupported protocol version in request.'},
|
{"error": "Unsupported protocol version in request."},
|
||||||
501,
|
501,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -133,7 +140,7 @@ def protocol_error(error: Any) -> Response:
|
|||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
def bad_json(error: Any) -> Response:
|
def bad_json(error: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Request JSON could not be decoded.'},
|
{"error": "Request JSON could not be decoded."},
|
||||||
500,
|
500,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -141,7 +148,7 @@ def bad_json(error: Any) -> Response:
|
|||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def unrecognized_object(error: Any) -> Response:
|
def unrecognized_object(error: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Unrecognized request game/version or object.'},
|
{"error": "Unrecognized request game/version or object."},
|
||||||
404,
|
404,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -149,50 +156,50 @@ def unrecognized_object(error: Any) -> Response:
|
|||||||
@app.errorhandler(405)
|
@app.errorhandler(405)
|
||||||
def invalid_request(error: Any) -> Response:
|
def invalid_request(error: Any) -> Response:
|
||||||
return jsonify_response(
|
return jsonify_response(
|
||||||
{'error': 'Invalid request URI or method.'},
|
{"error": "Invalid request URI or method."},
|
||||||
405,
|
405,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/<path:path>', methods=['GET', 'POST'])
|
@app.route("/<path:path>", methods=["GET", "POST"])
|
||||||
@authrequired
|
@authrequired
|
||||||
def catch_all(path: str) -> Response:
|
def catch_all(path: str) -> Response:
|
||||||
abort(405)
|
abort(405)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET', 'POST'])
|
@app.route("/", methods=["GET", "POST"])
|
||||||
@authrequired
|
@authrequired
|
||||||
@jsonify
|
@jsonify
|
||||||
def info() -> Dict[str, Any]:
|
def info() -> Dict[str, Any]:
|
||||||
requestdata = request.get_json()
|
requestdata = request.get_json()
|
||||||
if requestdata is None:
|
if requestdata is None:
|
||||||
raise APIException('Request JSON could not be decoded.')
|
raise APIException("Request JSON could not be decoded.")
|
||||||
if requestdata:
|
if requestdata:
|
||||||
raise APIException('Unrecognized parameters for request.')
|
raise APIException("Unrecognized parameters for request.")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'versions': SUPPORTED_VERSIONS,
|
"versions": SUPPORTED_VERSIONS,
|
||||||
'name': g.config.name,
|
"name": g.config.name,
|
||||||
'email': g.config.email,
|
"email": g.config.email,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/<protoversion>/<requestgame>/<requestversion>', methods=['GET', 'POST'])
|
@app.route("/<protoversion>/<requestgame>/<requestversion>", methods=["GET", "POST"])
|
||||||
@authrequired
|
@authrequired
|
||||||
@jsonify
|
@jsonify
|
||||||
def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str, Any]:
|
def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str, Any]:
|
||||||
requestdata = request.get_json()
|
requestdata = request.get_json()
|
||||||
for expected in ['type', 'ids', 'objects']:
|
for expected in ["type", "ids", "objects"]:
|
||||||
if expected not in requestdata:
|
if expected not in requestdata:
|
||||||
raise APIException('Missing parameters for request.')
|
raise APIException("Missing parameters for request.")
|
||||||
for param in requestdata:
|
for param in requestdata:
|
||||||
if param not in ['type', 'ids', 'objects', 'since', 'until']:
|
if param not in ["type", "ids", "objects", "since", "until"]:
|
||||||
raise APIException('Unrecognized parameters for request.')
|
raise APIException("Unrecognized parameters for request.")
|
||||||
|
|
||||||
args = copy.deepcopy(requestdata)
|
args = copy.deepcopy(requestdata)
|
||||||
del args['type']
|
del args["type"]
|
||||||
del args['ids']
|
del args["ids"]
|
||||||
del args['objects']
|
del args["objects"]
|
||||||
|
|
||||||
if protoversion not in SUPPORTED_VERSIONS:
|
if protoversion not in SUPPORTED_VERSIONS:
|
||||||
# Don't know about this protocol version
|
# 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.
|
# Figure out what games we support based on config, and map those.
|
||||||
gamemapping = {}
|
gamemapping = {}
|
||||||
for (gameid, constant) in [
|
for (gameid, constant) in [
|
||||||
('ddr', GameConstants.DDR),
|
("ddr", GameConstants.DDR),
|
||||||
('iidx', GameConstants.IIDX),
|
("iidx", GameConstants.IIDX),
|
||||||
('jubeat', GameConstants.JUBEAT),
|
("jubeat", GameConstants.JUBEAT),
|
||||||
('museca', GameConstants.MUSECA),
|
("museca", GameConstants.MUSECA),
|
||||||
('popnmusic', GameConstants.POPN_MUSIC),
|
("popnmusic", GameConstants.POPN_MUSIC),
|
||||||
('reflecbeat', GameConstants.REFLEC_BEAT),
|
("reflecbeat", GameConstants.REFLEC_BEAT),
|
||||||
('soundvoltex', GameConstants.SDVX),
|
("soundvoltex", GameConstants.SDVX),
|
||||||
]:
|
]:
|
||||||
if constant in g.config.support:
|
if constant in g.config.support:
|
||||||
gamemapping[gameid] = constant
|
gamemapping[gameid] = constant
|
||||||
@ -216,73 +223,77 @@ def lookup(protoversion: str, requestgame: str, requestversion: str) -> Dict[str
|
|||||||
# Don't support this game!
|
# Don't support this game!
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
if requestversion[0] == 'o':
|
if requestversion[0] == "o":
|
||||||
omnimix = True
|
omnimix = True
|
||||||
requestversion = requestversion[1:]
|
requestversion = requestversion[1:]
|
||||||
else:
|
else:
|
||||||
omnimix = False
|
omnimix = False
|
||||||
|
|
||||||
version = {
|
version = (
|
||||||
|
{
|
||||||
GameConstants.DDR: {
|
GameConstants.DDR: {
|
||||||
'12': VersionConstants.DDR_X2,
|
"12": VersionConstants.DDR_X2,
|
||||||
'13': VersionConstants.DDR_X3_VS_2NDMIX,
|
"13": VersionConstants.DDR_X3_VS_2NDMIX,
|
||||||
'14': VersionConstants.DDR_2013,
|
"14": VersionConstants.DDR_2013,
|
||||||
'15': VersionConstants.DDR_2014,
|
"15": VersionConstants.DDR_2014,
|
||||||
'16': VersionConstants.DDR_ACE,
|
"16": VersionConstants.DDR_ACE,
|
||||||
},
|
},
|
||||||
GameConstants.IIDX: {
|
GameConstants.IIDX: {
|
||||||
'20': VersionConstants.IIDX_TRICORO,
|
"20": VersionConstants.IIDX_TRICORO,
|
||||||
'21': VersionConstants.IIDX_SPADA,
|
"21": VersionConstants.IIDX_SPADA,
|
||||||
'22': VersionConstants.IIDX_PENDUAL,
|
"22": VersionConstants.IIDX_PENDUAL,
|
||||||
'23': VersionConstants.IIDX_COPULA,
|
"23": VersionConstants.IIDX_COPULA,
|
||||||
'24': VersionConstants.IIDX_SINOBUZ,
|
"24": VersionConstants.IIDX_SINOBUZ,
|
||||||
'25': VersionConstants.IIDX_CANNON_BALLERS,
|
"25": VersionConstants.IIDX_CANNON_BALLERS,
|
||||||
'26': VersionConstants.IIDX_ROOTAGE,
|
"26": VersionConstants.IIDX_ROOTAGE,
|
||||||
},
|
},
|
||||||
GameConstants.JUBEAT: {
|
GameConstants.JUBEAT: {
|
||||||
'5': VersionConstants.JUBEAT_SAUCER,
|
"5": VersionConstants.JUBEAT_SAUCER,
|
||||||
'5a': VersionConstants.JUBEAT_SAUCER_FULFILL,
|
"5a": VersionConstants.JUBEAT_SAUCER_FULFILL,
|
||||||
'6': VersionConstants.JUBEAT_PROP,
|
"6": VersionConstants.JUBEAT_PROP,
|
||||||
'7': VersionConstants.JUBEAT_QUBELL,
|
"7": VersionConstants.JUBEAT_QUBELL,
|
||||||
'8': VersionConstants.JUBEAT_CLAN,
|
"8": VersionConstants.JUBEAT_CLAN,
|
||||||
'9': VersionConstants.JUBEAT_FESTO,
|
"9": VersionConstants.JUBEAT_FESTO,
|
||||||
},
|
},
|
||||||
GameConstants.MUSECA: {
|
GameConstants.MUSECA: {
|
||||||
'1': VersionConstants.MUSECA,
|
"1": VersionConstants.MUSECA,
|
||||||
'1p': VersionConstants.MUSECA_1_PLUS,
|
"1p": VersionConstants.MUSECA_1_PLUS,
|
||||||
},
|
},
|
||||||
GameConstants.POPN_MUSIC: {
|
GameConstants.POPN_MUSIC: {
|
||||||
'19': VersionConstants.POPN_MUSIC_TUNE_STREET,
|
"19": VersionConstants.POPN_MUSIC_TUNE_STREET,
|
||||||
'20': VersionConstants.POPN_MUSIC_FANTASIA,
|
"20": VersionConstants.POPN_MUSIC_FANTASIA,
|
||||||
'21': VersionConstants.POPN_MUSIC_SUNNY_PARK,
|
"21": VersionConstants.POPN_MUSIC_SUNNY_PARK,
|
||||||
'22': VersionConstants.POPN_MUSIC_LAPISTORIA,
|
"22": VersionConstants.POPN_MUSIC_LAPISTORIA,
|
||||||
'23': VersionConstants.POPN_MUSIC_ECLALE,
|
"23": VersionConstants.POPN_MUSIC_ECLALE,
|
||||||
'24': VersionConstants.POPN_MUSIC_USANEKO,
|
"24": VersionConstants.POPN_MUSIC_USANEKO,
|
||||||
},
|
},
|
||||||
GameConstants.REFLEC_BEAT: {
|
GameConstants.REFLEC_BEAT: {
|
||||||
'1': VersionConstants.REFLEC_BEAT,
|
"1": VersionConstants.REFLEC_BEAT,
|
||||||
'2': VersionConstants.REFLEC_BEAT_LIMELIGHT,
|
"2": VersionConstants.REFLEC_BEAT_LIMELIGHT,
|
||||||
# We don't support non-final COLETTE, so just return scores for
|
# We don't support non-final COLETTE, so just return scores for
|
||||||
# final colette to any network that asks.
|
# final colette to any network that asks.
|
||||||
'3w': VersionConstants.REFLEC_BEAT_COLETTE,
|
"3w": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||||
'3sp': VersionConstants.REFLEC_BEAT_COLETTE,
|
"3sp": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||||
'3su': VersionConstants.REFLEC_BEAT_COLETTE,
|
"3su": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||||
'3a': VersionConstants.REFLEC_BEAT_COLETTE,
|
"3a": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||||
'3as': VersionConstants.REFLEC_BEAT_COLETTE,
|
"3as": VersionConstants.REFLEC_BEAT_COLETTE,
|
||||||
# We don't support groovin'!!, so just return upper scores.
|
# We don't support groovin'!!, so just return upper scores.
|
||||||
'4': VersionConstants.REFLEC_BEAT_GROOVIN,
|
"4": VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||||
'4u': VersionConstants.REFLEC_BEAT_GROOVIN,
|
"4u": VersionConstants.REFLEC_BEAT_GROOVIN,
|
||||||
'5': VersionConstants.REFLEC_BEAT_VOLZZA,
|
"5": VersionConstants.REFLEC_BEAT_VOLZZA,
|
||||||
'5a': VersionConstants.REFLEC_BEAT_VOLZZA_2,
|
"5a": VersionConstants.REFLEC_BEAT_VOLZZA_2,
|
||||||
'6': VersionConstants.REFLEC_BEAT_REFLESIA,
|
"6": VersionConstants.REFLEC_BEAT_REFLESIA,
|
||||||
},
|
},
|
||||||
GameConstants.SDVX: {
|
GameConstants.SDVX: {
|
||||||
'1': VersionConstants.SDVX_BOOTH,
|
"1": VersionConstants.SDVX_BOOTH,
|
||||||
'2': VersionConstants.SDVX_INFINITE_INFECTION,
|
"2": VersionConstants.SDVX_INFINITE_INFECTION,
|
||||||
'3': VersionConstants.SDVX_GRAVITY_WARS,
|
"3": VersionConstants.SDVX_GRAVITY_WARS,
|
||||||
'4': VersionConstants.SDVX_HEAVENLY_HAVEN,
|
"4": VersionConstants.SDVX_HEAVENLY_HAVEN,
|
||||||
},
|
},
|
||||||
}.get(game, {}).get(requestversion)
|
}
|
||||||
|
.get(game, {})
|
||||||
|
.get(requestversion)
|
||||||
|
)
|
||||||
if version is None:
|
if version is None:
|
||||||
# Don't support this version!
|
# Don't support this version!
|
||||||
abort(404)
|
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.
|
# Attempt to coerce ID type. If we fail, provide the correct failure message.
|
||||||
idtype = None
|
idtype = None
|
||||||
try:
|
try:
|
||||||
idtype = APIConstants(requestdata['type'])
|
idtype = APIConstants(requestdata["type"])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
if idtype is None:
|
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.
|
# 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:
|
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]:
|
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:
|
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:
|
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 = {}
|
responsedata = {}
|
||||||
for obj in requestdata['objects']:
|
for obj in requestdata["objects"]:
|
||||||
handler = {
|
handler = {
|
||||||
'records': RecordsObject,
|
"records": RecordsObject,
|
||||||
'profile': ProfileObject,
|
"profile": ProfileObject,
|
||||||
'statistics': StatisticsObject,
|
"statistics": StatisticsObject,
|
||||||
'catalog': CatalogObject,
|
"catalog": CatalogObject,
|
||||||
}.get(obj)
|
}.get(obj)
|
||||||
if handler is None:
|
if handler is None:
|
||||||
# Don't support this object type
|
# 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)
|
inst = handler(g.data, game, version, omnimix)
|
||||||
try:
|
try:
|
||||||
fetchmethod = getattr(inst, f'fetch_{protoversion}')
|
fetchmethod = getattr(inst, f"fetch_{protoversion}")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Don't know how to handle this object for this version
|
# Don't know how to handle this object for this version
|
||||||
abort(501)
|
abort(501)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
class APIException(Exception):
|
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.message = msg
|
||||||
self.code = code
|
self.code = code
|
||||||
|
@ -14,11 +14,15 @@ class BaseObject:
|
|||||||
various fetch versions.
|
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.data = data
|
||||||
self.game = game
|
self.game = game
|
||||||
self.version = version
|
self.version = version
|
||||||
self.omnimix = omnimix
|
self.omnimix = omnimix
|
||||||
|
|
||||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> Any:
|
def fetch_v1(
|
||||||
raise APIException('Object fetch not supported for this version!')
|
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):
|
class CatalogObject(BaseObject):
|
||||||
|
|
||||||
def __format_ddr_song(self, song: Song) -> Dict[str, Any]:
|
def __format_ddr_song(self, song: Song) -> Dict[str, Any]:
|
||||||
groove = song.data.get_dict('groove')
|
groove = song.data.get_dict("groove")
|
||||||
return {
|
return {
|
||||||
'editid': str(song.data.get_int('edit_id')),
|
"editid": str(song.data.get_int("edit_id")),
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'bpm_min': song.data.get_int('bpm_min'),
|
"bpm_min": song.data.get_int("bpm_min"),
|
||||||
'bpm_max': song.data.get_int('bpm_max'),
|
"bpm_max": song.data.get_int("bpm_max"),
|
||||||
'category': str(song.data.get_int('category')),
|
"category": str(song.data.get_int("category")),
|
||||||
'groove': {
|
"groove": {
|
||||||
'air': groove.get_int('air'),
|
"air": groove.get_int("air"),
|
||||||
'chaos': groove.get_int('chaos'),
|
"chaos": groove.get_int("chaos"),
|
||||||
'freeze': groove.get_int('freeze'),
|
"freeze": groove.get_int("freeze"),
|
||||||
'stream': groove.get_int('stream'),
|
"stream": groove.get_int("stream"),
|
||||||
'voltage': groove.get_int('voltage'),
|
"voltage": groove.get_int("voltage"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_iidx_song(self, song: Song) -> Dict[str, Any]:
|
def __format_iidx_song(self, song: Song) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'bpm_min': song.data.get_int('bpm_min'),
|
"bpm_min": song.data.get_int("bpm_min"),
|
||||||
'bpm_max': song.data.get_int('bpm_max'),
|
"bpm_max": song.data.get_int("bpm_max"),
|
||||||
'notecount': song.data.get_int('notecount'),
|
"notecount": song.data.get_int("notecount"),
|
||||||
'category': str(int(song.id / 1000)),
|
"category": str(int(song.id / 1000)),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_jubeat_song(self, song: Song) -> Dict[str, Any]:
|
def __format_jubeat_song(self, song: Song) -> Dict[str, Any]:
|
||||||
@ -46,67 +45,69 @@ class CatalogObject(BaseObject):
|
|||||||
6: VersionConstants.JUBEAT_PROP,
|
6: VersionConstants.JUBEAT_PROP,
|
||||||
7: VersionConstants.JUBEAT_QUBELL,
|
7: VersionConstants.JUBEAT_QUBELL,
|
||||||
8: VersionConstants.JUBEAT_CLAN,
|
8: VersionConstants.JUBEAT_CLAN,
|
||||||
9: VersionConstants.JUBEAT_FESTO
|
9: VersionConstants.JUBEAT_FESTO,
|
||||||
}.get(int(song.id / 10000000), VersionConstants.JUBEAT)
|
}.get(int(song.id / 10000000), VersionConstants.JUBEAT)
|
||||||
# Map the category to the version numbers defined on BEMAPI.
|
# Map the category to the version numbers defined on BEMAPI.
|
||||||
categorymapping = {
|
categorymapping = {
|
||||||
VersionConstants.JUBEAT: '1',
|
VersionConstants.JUBEAT: "1",
|
||||||
VersionConstants.JUBEAT_RIPPLES: '2',
|
VersionConstants.JUBEAT_RIPPLES: "2",
|
||||||
VersionConstants.JUBEAT_RIPPLES_APPEND: '2a',
|
VersionConstants.JUBEAT_RIPPLES_APPEND: "2a",
|
||||||
VersionConstants.JUBEAT_KNIT: '3',
|
VersionConstants.JUBEAT_KNIT: "3",
|
||||||
VersionConstants.JUBEAT_KNIT_APPEND: '3a',
|
VersionConstants.JUBEAT_KNIT_APPEND: "3a",
|
||||||
VersionConstants.JUBEAT_COPIOUS: '4',
|
VersionConstants.JUBEAT_COPIOUS: "4",
|
||||||
VersionConstants.JUBEAT_COPIOUS_APPEND: '4a',
|
VersionConstants.JUBEAT_COPIOUS_APPEND: "4a",
|
||||||
VersionConstants.JUBEAT_SAUCER: '5',
|
VersionConstants.JUBEAT_SAUCER: "5",
|
||||||
VersionConstants.JUBEAT_SAUCER_FULFILL: '5a',
|
VersionConstants.JUBEAT_SAUCER_FULFILL: "5a",
|
||||||
VersionConstants.JUBEAT_PROP: '6',
|
VersionConstants.JUBEAT_PROP: "6",
|
||||||
VersionConstants.JUBEAT_QUBELL: '7',
|
VersionConstants.JUBEAT_QUBELL: "7",
|
||||||
VersionConstants.JUBEAT_CLAN: '8',
|
VersionConstants.JUBEAT_CLAN: "8",
|
||||||
VersionConstants.JUBEAT_FESTO: '9',
|
VersionConstants.JUBEAT_FESTO: "9",
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'category': categorymapping.get(song.data.get_int('version', defaultcategory), '1'),
|
"category": categorymapping.get(
|
||||||
'bpm_min': song.data.get_int('bpm_min'),
|
song.data.get_int("version", defaultcategory), "1"
|
||||||
'bpm_max': song.data.get_int('bpm_max'),
|
),
|
||||||
|
"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]:
|
def __format_museca_song(self, song: Song) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'bpm_min': song.data.get_int('bpm_min'),
|
"bpm_min": song.data.get_int("bpm_min"),
|
||||||
'bpm_max': song.data.get_int('bpm_max'),
|
"bpm_max": song.data.get_int("bpm_max"),
|
||||||
'limited': song.data.get_int('limited'),
|
"limited": song.data.get_int("limited"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_popn_song(self, song: Song) -> Dict[str, Any]:
|
def __format_popn_song(self, song: Song) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'category': song.data.get_str('category'),
|
"category": song.data.get_str("category"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_reflec_song(self, song: Song) -> Dict[str, Any]:
|
def __format_reflec_song(self, song: Song) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'category': str(song.data.get_int('folder')),
|
"category": str(song.data.get_int("folder")),
|
||||||
'musicid': song.data.get_str('chart_id'),
|
"musicid": song.data.get_str("chart_id"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_sdvx_song(self, song: Song) -> Dict[str, Any]:
|
def __format_sdvx_song(self, song: Song) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'difficulty': song.data.get_int('difficulty'),
|
"difficulty": song.data.get_int("difficulty"),
|
||||||
'bpm_min': song.data.get_int('bpm_min'),
|
"bpm_min": song.data.get_int("bpm_min"),
|
||||||
'bpm_max': song.data.get_int('bpm_max'),
|
"bpm_max": song.data.get_int("bpm_max"),
|
||||||
'limited': song.data.get_int('limited'),
|
"limited": song.data.get_int("limited"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_song(self, song: Song) -> Dict[str, Any]:
|
def __format_song(self, song: Song) -> Dict[str, Any]:
|
||||||
base = {
|
base = {
|
||||||
'song': str(song.id),
|
"song": str(song.id),
|
||||||
'chart': str(song.chart),
|
"chart": str(song.chart),
|
||||||
'title': song.name or "",
|
"title": song.name or "",
|
||||||
'artist': song.artist or "",
|
"artist": song.artist or "",
|
||||||
'genre': song.genre or "",
|
"genre": song.genre or "",
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.game == GameConstants.DDR:
|
if self.game == GameConstants.DDR:
|
||||||
@ -198,7 +199,7 @@ class CatalogObject(BaseObject):
|
|||||||
"type": item.type[3:],
|
"type": item.type[3:],
|
||||||
}
|
}
|
||||||
for item in items
|
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:
|
else:
|
||||||
return self.version
|
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
|
# Verify IDs
|
||||||
if idtype != APIConstants.ID_TYPE_SERVER:
|
if idtype != APIConstants.ID_TYPE_SERVER:
|
||||||
raise APIException(
|
raise APIException(
|
||||||
'Unsupported ID for lookup!',
|
"Unsupported ID for lookup!",
|
||||||
405,
|
405,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch the songs
|
# Fetch the songs
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
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
|
# 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
|
# 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
|
# for different prefectures and slightly different charts. So, we need to copy
|
||||||
@ -255,7 +261,7 @@ class CatalogObject(BaseObject):
|
|||||||
)
|
)
|
||||||
songs.extend(additions)
|
songs.extend(additions)
|
||||||
retval = {
|
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
|
# Fetch any optional extras per-game, return
|
||||||
|
@ -7,24 +7,23 @@ from bemani.data import UserID
|
|||||||
|
|
||||||
|
|
||||||
class ProfileObject(BaseObject):
|
class ProfileObject(BaseObject):
|
||||||
|
|
||||||
def __format_ddr_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
def __format_ddr_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||||
return {
|
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]:
|
def __format_iidx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||||
qpro = profile.get_dict('qpro')
|
qpro = profile.get_dict("qpro")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'area': profile.get_int('pid', -1),
|
"area": profile.get_int("pid", -1),
|
||||||
'qpro': {
|
"qpro": {
|
||||||
'head': qpro.get_int('head', -1) if exact else -1,
|
"head": qpro.get_int("head", -1) if exact else -1,
|
||||||
'hair': qpro.get_int('hair', -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,
|
"face": qpro.get_int("face", -1) if exact else -1,
|
||||||
'body': qpro.get_int('body', -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,
|
"hand": qpro.get_int("hand", -1) if exact else -1,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_jubeat_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
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]:
|
def __format_popn_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||||
return {
|
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]:
|
def __format_reflec_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||||
return {
|
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]:
|
def __format_sdvx_profile(self, profile: Profile, exact: bool) -> Dict[str, Any]:
|
||||||
return {}
|
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 = {
|
base = {
|
||||||
'name': profile.get_str('name'),
|
"name": profile.get_str("name"),
|
||||||
'cards': cardids,
|
"cards": cardids,
|
||||||
'registered': settings.get_int('first_play_timestamp', -1),
|
"registered": settings.get_int("first_play_timestamp", -1),
|
||||||
'updated': settings.get_int('last_play_timestamp', -1),
|
"updated": settings.get_int("last_play_timestamp", -1),
|
||||||
'plays': settings.get_int('total_plays', -1),
|
"plays": settings.get_int("total_plays", -1),
|
||||||
'match': 'exact' if exact else 'partial',
|
"match": "exact" if exact else "partial",
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.game == GameConstants.DDR:
|
if self.game == GameConstants.DDR:
|
||||||
@ -73,19 +74,23 @@ class ProfileObject(BaseObject):
|
|||||||
|
|
||||||
return base
|
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
|
# Fetch the profiles
|
||||||
profiles: List[Tuple[UserID, Profile]] = []
|
profiles: List[Tuple[UserID, Profile]] = []
|
||||||
if idtype == APIConstants.ID_TYPE_SERVER:
|
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:
|
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||||
raise APIException(
|
raise APIException(
|
||||||
'Unsupported ID for lookup!',
|
"Unsupported ID for lookup!",
|
||||||
405,
|
405,
|
||||||
)
|
)
|
||||||
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||||
raise APIException(
|
raise APIException(
|
||||||
'Unsupported ID for lookup!',
|
"Unsupported ID for lookup!",
|
||||||
405,
|
405,
|
||||||
)
|
)
|
||||||
elif idtype == APIConstants.ID_TYPE_CARD:
|
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
|
# 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
|
# 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.
|
# 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:
|
if profile is not None:
|
||||||
profiles.append((userid, profile))
|
profiles.append((userid, profile))
|
||||||
else:
|
else:
|
||||||
raise APIException('Invalid ID type!')
|
raise APIException("Invalid ID type!")
|
||||||
|
|
||||||
# Now, fetch the users, and filter out profiles belonging to orphaned users
|
# Now, fetch the users, and filter out profiles belonging to orphaned users
|
||||||
retval: List[Dict[str, Any]] = []
|
retval: List[Dict[str, Any]] = []
|
||||||
@ -126,6 +133,13 @@ class ProfileObject(BaseObject):
|
|||||||
if settings is None:
|
if settings is None:
|
||||||
settings = ValidatedDict({})
|
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
|
return retval
|
||||||
|
@ -7,15 +7,14 @@ from bemani.data import Score, UserID
|
|||||||
|
|
||||||
|
|
||||||
class RecordsObject(BaseObject):
|
class RecordsObject(BaseObject):
|
||||||
|
|
||||||
def __format_ddr_record(self, record: Score) -> Dict[str, Any]:
|
def __format_ddr_record(self, record: Score) -> Dict[str, Any]:
|
||||||
halo = {
|
halo = {
|
||||||
DBConstants.DDR_HALO_NONE: 'none',
|
DBConstants.DDR_HALO_NONE: "none",
|
||||||
DBConstants.DDR_HALO_GOOD_FULL_COMBO: 'gfc',
|
DBConstants.DDR_HALO_GOOD_FULL_COMBO: "gfc",
|
||||||
DBConstants.DDR_HALO_GREAT_FULL_COMBO: 'fc',
|
DBConstants.DDR_HALO_GREAT_FULL_COMBO: "fc",
|
||||||
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: 'pfc',
|
DBConstants.DDR_HALO_PERFECT_FULL_COMBO: "pfc",
|
||||||
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: 'mfc',
|
DBConstants.DDR_HALO_MARVELOUS_FULL_COMBO: "mfc",
|
||||||
}.get(record.data.get_int('halo'), 'none')
|
}.get(record.data.get_int("halo"), "none")
|
||||||
rank = {
|
rank = {
|
||||||
DBConstants.DDR_RANK_AAA: "AAA",
|
DBConstants.DDR_RANK_AAA: "AAA",
|
||||||
DBConstants.DDR_RANK_AA_PLUS: "AA+",
|
DBConstants.DDR_RANK_AA_PLUS: "AA+",
|
||||||
@ -33,175 +32,175 @@ class RecordsObject(BaseObject):
|
|||||||
DBConstants.DDR_RANK_D_PLUS: "D+",
|
DBConstants.DDR_RANK_D_PLUS: "D+",
|
||||||
DBConstants.DDR_RANK_D: "D",
|
DBConstants.DDR_RANK_D: "D",
|
||||||
DBConstants.DDR_RANK_E: "E",
|
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:
|
if self.version == VersionConstants.DDR_ACE:
|
||||||
# DDR Ace is specia
|
# 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:
|
else:
|
||||||
if 'trace' not in record.data:
|
if "trace" not in record.data:
|
||||||
ghost = []
|
ghost = []
|
||||||
else:
|
else:
|
||||||
ghost = record.data.get_int_array('trace', len(record.data['trace']))
|
ghost = record.data.get_int_array("trace", len(record.data["trace"]))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'rank': rank,
|
"rank": rank,
|
||||||
'halo': halo,
|
"halo": halo,
|
||||||
'combo': record.data.get_int('combo'),
|
"combo": record.data.get_int("combo"),
|
||||||
'ghost': ghost,
|
"ghost": ghost,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_iidx_record(self, record: Score) -> Dict[str, Any]:
|
def __format_iidx_record(self, record: Score) -> Dict[str, Any]:
|
||||||
status = {
|
status = {
|
||||||
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: 'np',
|
DBConstants.IIDX_CLEAR_STATUS_NO_PLAY: "np",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_FAILED: 'failed',
|
DBConstants.IIDX_CLEAR_STATUS_FAILED: "failed",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: 'ac',
|
DBConstants.IIDX_CLEAR_STATUS_ASSIST_CLEAR: "ac",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: 'ec',
|
DBConstants.IIDX_CLEAR_STATUS_EASY_CLEAR: "ec",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_CLEAR: 'nc',
|
DBConstants.IIDX_CLEAR_STATUS_CLEAR: "nc",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: 'hc',
|
DBConstants.IIDX_CLEAR_STATUS_HARD_CLEAR: "hc",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: 'exhc',
|
DBConstants.IIDX_CLEAR_STATUS_EX_HARD_CLEAR: "exhc",
|
||||||
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: 'fc',
|
DBConstants.IIDX_CLEAR_STATUS_FULL_COMBO: "fc",
|
||||||
}.get(record.data.get_int('clear_status'), 'np')
|
}.get(record.data.get_int("clear_status"), "np")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'status': status,
|
"status": status,
|
||||||
'miss': record.data.get_int('miss_count', -1),
|
"miss": record.data.get_int("miss_count", -1),
|
||||||
'ghost': [b for b in record.data.get_bytes('ghost')],
|
"ghost": [b for b in record.data.get_bytes("ghost")],
|
||||||
'pgreat': record.data.get_int('pgreats', -1),
|
"pgreat": record.data.get_int("pgreats", -1),
|
||||||
'great': record.data.get_int('greats', -1),
|
"great": record.data.get_int("greats", -1),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_jubeat_record(self, record: Score) -> Dict[str, Any]:
|
def __format_jubeat_record(self, record: Score) -> Dict[str, Any]:
|
||||||
status = {
|
status = {
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: 'failed',
|
DBConstants.JUBEAT_PLAY_MEDAL_FAILED: "failed",
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: 'cleared',
|
DBConstants.JUBEAT_PLAY_MEDAL_CLEARED: "cleared",
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: 'nfc',
|
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: "nfc",
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: 'fc',
|
DBConstants.JUBEAT_PLAY_MEDAL_FULL_COMBO: "fc",
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: 'nec',
|
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: "nec",
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: 'exc',
|
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT: "exc",
|
||||||
}.get(record.data.get_int('medal'), 'failed')
|
}.get(record.data.get_int("medal"), "failed")
|
||||||
if 'ghost' not in record.data:
|
if "ghost" not in record.data:
|
||||||
ghost: List[int] = []
|
ghost: List[int] = []
|
||||||
else:
|
else:
|
||||||
ghost = record.data.get_int_array('ghost', len(record.data['ghost']))
|
ghost = record.data.get_int_array("ghost", len(record.data["ghost"]))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'status': status,
|
"status": status,
|
||||||
'combo': record.data.get_int('combo', -1),
|
"combo": record.data.get_int("combo", -1),
|
||||||
'ghost': ghost,
|
"ghost": ghost,
|
||||||
'music_rate': record.data.get_int('music_rate'),
|
"music_rate": record.data.get_int("music_rate"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_museca_record(self, record: Score) -> Dict[str, Any]:
|
def __format_museca_record(self, record: Score) -> Dict[str, Any]:
|
||||||
rank = {
|
rank = {
|
||||||
DBConstants.MUSECA_GRADE_DEATH: 'death',
|
DBConstants.MUSECA_GRADE_DEATH: "death",
|
||||||
DBConstants.MUSECA_GRADE_POOR: 'poor',
|
DBConstants.MUSECA_GRADE_POOR: "poor",
|
||||||
DBConstants.MUSECA_GRADE_MEDIOCRE: 'mediocre',
|
DBConstants.MUSECA_GRADE_MEDIOCRE: "mediocre",
|
||||||
DBConstants.MUSECA_GRADE_GOOD: 'good',
|
DBConstants.MUSECA_GRADE_GOOD: "good",
|
||||||
DBConstants.MUSECA_GRADE_GREAT: 'great',
|
DBConstants.MUSECA_GRADE_GREAT: "great",
|
||||||
DBConstants.MUSECA_GRADE_EXCELLENT: 'excellent',
|
DBConstants.MUSECA_GRADE_EXCELLENT: "excellent",
|
||||||
DBConstants.MUSECA_GRADE_SUPERB: 'superb',
|
DBConstants.MUSECA_GRADE_SUPERB: "superb",
|
||||||
DBConstants.MUSECA_GRADE_MASTERPIECE: 'masterpiece',
|
DBConstants.MUSECA_GRADE_MASTERPIECE: "masterpiece",
|
||||||
DBConstants.MUSECA_GRADE_PERFECT: 'perfect'
|
DBConstants.MUSECA_GRADE_PERFECT: "perfect",
|
||||||
}.get(record.data.get_int('grade'), 'death')
|
}.get(record.data.get_int("grade"), "death")
|
||||||
status = {
|
status = {
|
||||||
DBConstants.MUSECA_CLEAR_TYPE_FAILED: 'failed',
|
DBConstants.MUSECA_CLEAR_TYPE_FAILED: "failed",
|
||||||
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: 'cleared',
|
DBConstants.MUSECA_CLEAR_TYPE_CLEARED: "cleared",
|
||||||
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: 'fc',
|
DBConstants.MUSECA_CLEAR_TYPE_FULL_COMBO: "fc",
|
||||||
}.get(record.data.get_int('clear_type'), 'failed')
|
}.get(record.data.get_int("clear_type"), "failed")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'rank': rank,
|
"rank": rank,
|
||||||
'status': status,
|
"status": status,
|
||||||
'combo': record.data.get_int('combo', -1),
|
"combo": record.data.get_int("combo", -1),
|
||||||
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
|
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
|
||||||
'longrate': record.data.get_dict('stats').get_int('long_rate'),
|
"longrate": record.data.get_dict("stats").get_int("long_rate"),
|
||||||
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
|
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_popn_record(self, record: Score) -> Dict[str, Any]:
|
def __format_popn_record(self, record: Score) -> Dict[str, Any]:
|
||||||
status = {
|
status = {
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: 'cf',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: "cf",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: 'df',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: "df",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: 'sf',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: "sf",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: 'ec',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: "ec",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: 'cc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: "cc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: 'dc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: "dc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: 'sc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: "sc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: 'cfc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: "cfc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: 'dfc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: "dfc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: 'sfc',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: "sfc",
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: 'p',
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT: "p",
|
||||||
}.get(record.data.get_int('medal'), 'cf')
|
}.get(record.data.get_int("medal"), "cf")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'status': status,
|
"status": status,
|
||||||
'combo': record.data.get_int('combo', -1),
|
"combo": record.data.get_int("combo", -1),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_reflec_record(self, record: Score) -> Dict[str, Any]:
|
def __format_reflec_record(self, record: Score) -> Dict[str, Any]:
|
||||||
status = {
|
status = {
|
||||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: 'np',
|
DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: "np",
|
||||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: 'failed',
|
DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED: "failed",
|
||||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: 'cleared',
|
DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED: "cleared",
|
||||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: 'hc',
|
DBConstants.REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: "hc",
|
||||||
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: 'shc',
|
DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: "shc",
|
||||||
}.get(record.data.get_int('clear_type'), 'np')
|
}.get(record.data.get_int("clear_type"), "np")
|
||||||
halo = {
|
halo = {
|
||||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: 'none',
|
DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE: "none",
|
||||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: 'ac',
|
DBConstants.REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: "ac",
|
||||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: 'fc',
|
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: "fc",
|
||||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: 'fcaj',
|
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: "fcaj",
|
||||||
}.get(record.data.get_int('combo_type'), 'none')
|
}.get(record.data.get_int("combo_type"), "none")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'rate': record.data.get_int('achievement_rate'),
|
"rate": record.data.get_int("achievement_rate"),
|
||||||
'status': status,
|
"status": status,
|
||||||
'halo': halo,
|
"halo": halo,
|
||||||
'combo': record.data.get_int('combo', -1),
|
"combo": record.data.get_int("combo", -1),
|
||||||
'miss': record.data.get_int('miss_count', -1),
|
"miss": record.data.get_int("miss_count", -1),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_sdvx_record(self, record: Score) -> Dict[str, Any]:
|
def __format_sdvx_record(self, record: Score) -> Dict[str, Any]:
|
||||||
status = {
|
status = {
|
||||||
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: 'np',
|
DBConstants.SDVX_CLEAR_TYPE_NO_PLAY: "np",
|
||||||
DBConstants.SDVX_CLEAR_TYPE_FAILED: 'failed',
|
DBConstants.SDVX_CLEAR_TYPE_FAILED: "failed",
|
||||||
DBConstants.SDVX_CLEAR_TYPE_CLEAR: 'cleared',
|
DBConstants.SDVX_CLEAR_TYPE_CLEAR: "cleared",
|
||||||
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: 'hc',
|
DBConstants.SDVX_CLEAR_TYPE_HARD_CLEAR: "hc",
|
||||||
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: 'uc',
|
DBConstants.SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: "uc",
|
||||||
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: 'puc',
|
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: "puc",
|
||||||
}.get(record.data.get_int('clear_type'), 'np')
|
}.get(record.data.get_int("clear_type"), "np")
|
||||||
rank = {
|
rank = {
|
||||||
DBConstants.SDVX_GRADE_NO_PLAY: 'E',
|
DBConstants.SDVX_GRADE_NO_PLAY: "E",
|
||||||
DBConstants.SDVX_GRADE_D: 'D',
|
DBConstants.SDVX_GRADE_D: "D",
|
||||||
DBConstants.SDVX_GRADE_C: 'C',
|
DBConstants.SDVX_GRADE_C: "C",
|
||||||
DBConstants.SDVX_GRADE_B: 'B',
|
DBConstants.SDVX_GRADE_B: "B",
|
||||||
DBConstants.SDVX_GRADE_A: 'A',
|
DBConstants.SDVX_GRADE_A: "A",
|
||||||
DBConstants.SDVX_GRADE_A_PLUS: 'A+',
|
DBConstants.SDVX_GRADE_A_PLUS: "A+",
|
||||||
DBConstants.SDVX_GRADE_AA: 'AA',
|
DBConstants.SDVX_GRADE_AA: "AA",
|
||||||
DBConstants.SDVX_GRADE_AA_PLUS: 'AA+',
|
DBConstants.SDVX_GRADE_AA_PLUS: "AA+",
|
||||||
DBConstants.SDVX_GRADE_AAA: 'AAA',
|
DBConstants.SDVX_GRADE_AAA: "AAA",
|
||||||
DBConstants.SDVX_GRADE_AAA_PLUS: 'AAA+',
|
DBConstants.SDVX_GRADE_AAA_PLUS: "AAA+",
|
||||||
DBConstants.SDVX_GRADE_S: 'S',
|
DBConstants.SDVX_GRADE_S: "S",
|
||||||
}.get(record.data.get_int('grade'), 'E')
|
}.get(record.data.get_int("grade"), "E")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'status': status,
|
"status": status,
|
||||||
'rank': rank,
|
"rank": rank,
|
||||||
'combo': record.data.get_int('combo', -1),
|
"combo": record.data.get_int("combo", -1),
|
||||||
'buttonrate': record.data.get_dict('stats').get_int('btn_rate'),
|
"buttonrate": record.data.get_dict("stats").get_int("btn_rate"),
|
||||||
'longrate': record.data.get_dict('stats').get_int('long_rate'),
|
"longrate": record.data.get_dict("stats").get_int("long_rate"),
|
||||||
'volrate': record.data.get_dict('stats').get_int('vol_rate'),
|
"volrate": record.data.get_dict("stats").get_int("vol_rate"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __format_record(self, cardids: List[str], record: Score) -> Dict[str, Any]:
|
def __format_record(self, cardids: List[str], record: Score) -> Dict[str, Any]:
|
||||||
base = {
|
base = {
|
||||||
'cards': cardids,
|
"cards": cardids,
|
||||||
'song': str(record.id),
|
"song": str(record.id),
|
||||||
'chart': str(record.chart),
|
"chart": str(record.chart),
|
||||||
'points': record.points,
|
"points": record.points,
|
||||||
'timestamp': record.timestamp,
|
"timestamp": record.timestamp,
|
||||||
'updated': record.update,
|
"updated": record.update,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.game == GameConstants.DDR:
|
if self.game == GameConstants.DDR:
|
||||||
@ -231,9 +230,11 @@ class RecordsObject(BaseObject):
|
|||||||
else:
|
else:
|
||||||
return self.version
|
return self.version
|
||||||
|
|
||||||
def fetch_v1(self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
def fetch_v1(
|
||||||
since = params.get('since')
|
self, idtype: APIConstants, ids: List[str], params: Dict[str, Any]
|
||||||
until = params.get('until')
|
) -> List[Dict[str, Any]]:
|
||||||
|
since = params.get("since")
|
||||||
|
until = params.get("until")
|
||||||
|
|
||||||
# Fetch the scores
|
# Fetch the scores
|
||||||
records: List[Tuple[UserID, Score]] = []
|
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.
|
# 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
|
# If we did, it would miss higher scores earned before since or after until, and
|
||||||
# incorrectly report records.
|
# 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:
|
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||||
if len(ids) == 1:
|
if len(ids) == 1:
|
||||||
songid = int(ids[0])
|
songid = int(ids[0])
|
||||||
@ -249,14 +252,25 @@ class RecordsObject(BaseObject):
|
|||||||
else:
|
else:
|
||||||
songid = int(ids[0])
|
songid = int(ids[0])
|
||||||
chart = int(ids[1])
|
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:
|
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||||
songid = int(ids[0])
|
songid = int(ids[0])
|
||||||
chart = int(ids[1])
|
chart = int(ids[1])
|
||||||
cardid = ids[2]
|
cardid = ids[2]
|
||||||
userid = self.data.local.user.from_cardid(cardid)
|
userid = self.data.local.user.from_cardid(cardid)
|
||||||
if userid is not None:
|
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:
|
if score is not None:
|
||||||
records.append((userid, score))
|
records.append((userid, score))
|
||||||
elif idtype == APIConstants.ID_TYPE_CARD:
|
elif idtype == APIConstants.ID_TYPE_CARD:
|
||||||
@ -270,9 +284,20 @@ class RecordsObject(BaseObject):
|
|||||||
continue
|
continue
|
||||||
users.add(userid)
|
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:
|
else:
|
||||||
raise APIException('Invalid ID type!')
|
raise APIException("Invalid ID type!")
|
||||||
|
|
||||||
# Now, fetch the users, and filter out scores belonging to orphaned users
|
# Now, fetch the users, and filter out scores belonging to orphaned users
|
||||||
id_to_cards: Dict[UserID, List[str]] = {}
|
id_to_cards: Dict[UserID, List[str]] = {}
|
||||||
|
@ -7,20 +7,21 @@ from bemani.data import Attempt, UserID
|
|||||||
|
|
||||||
|
|
||||||
class StatisticsObject(BaseObject):
|
class StatisticsObject(BaseObject):
|
||||||
|
|
||||||
def __format_statistics(self, stats: Dict[str, Any]) -> Dict[str, Any]:
|
def __format_statistics(self, stats: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
'cards': [],
|
"cards": [],
|
||||||
'song': str(stats['id']),
|
"song": str(stats["id"]),
|
||||||
'chart': str(stats['chart']),
|
"chart": str(stats["chart"]),
|
||||||
'plays': stats.get('plays', -1),
|
"plays": stats.get("plays", -1),
|
||||||
'clears': stats.get('clears', -1),
|
"clears": stats.get("clears", -1),
|
||||||
'combos': stats.get('combos', -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 = self.__format_statistics(stats)
|
||||||
base['cards'] = cardids
|
base["cards"] = cardids
|
||||||
return base
|
return base
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -42,11 +43,20 @@ class StatisticsObject(BaseObject):
|
|||||||
}:
|
}:
|
||||||
return True
|
return True
|
||||||
if self.game == GameConstants.IIDX:
|
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:
|
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:
|
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
|
return False
|
||||||
|
|
||||||
@ -55,29 +65,39 @@ class StatisticsObject(BaseObject):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if self.game == GameConstants.DDR:
|
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:
|
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:
|
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:
|
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:
|
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_CIRCLE_FAILED,
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED,
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED,
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED,
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FAILED,
|
||||||
]
|
]
|
||||||
if self.game == GameConstants.REFLEC_BEAT:
|
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 (
|
return (
|
||||||
attempt.data.get_int('grade') != DBConstants.SDVX_GRADE_NO_PLAY and
|
attempt.data.get_int("clear_type")
|
||||||
attempt.data.get_int('clear_type') not in [
|
!= 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_NO_PLAY,
|
||||||
DBConstants.SDVX_CLEAR_TYPE_FAILED,
|
DBConstants.SDVX_CLEAR_TYPE_FAILED,
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -86,31 +106,37 @@ class StatisticsObject(BaseObject):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if self.game == GameConstants.DDR:
|
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:
|
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:
|
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_FULL_COMBO,
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT,
|
DBConstants.JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT,
|
||||||
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT,
|
DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT,
|
||||||
]
|
]
|
||||||
if self.game == GameConstants.MUSECA:
|
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:
|
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_CIRCLE_FULL_COMBO,
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO,
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO,
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO,
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO,
|
||||||
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT,
|
DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT,
|
||||||
]
|
]
|
||||||
if self.game == GameConstants.REFLEC_BEAT:
|
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,
|
||||||
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST,
|
DBConstants.REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST,
|
||||||
]
|
]
|
||||||
if self.game == GameConstants.SDVX:
|
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_ULTIMATE_CHAIN,
|
||||||
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN,
|
DBConstants.SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN,
|
||||||
]
|
]
|
||||||
@ -125,29 +151,31 @@ class StatisticsObject(BaseObject):
|
|||||||
stats[attempt.id] = {}
|
stats[attempt.id] = {}
|
||||||
if attempt.chart not in stats[attempt.id]:
|
if attempt.chart not in stats[attempt.id]:
|
||||||
stats[attempt.id][attempt.chart] = {
|
stats[attempt.id][attempt.chart] = {
|
||||||
'plays': 0,
|
"plays": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'combos': 0,
|
"combos": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.__is_play(attempt):
|
if self.__is_play(attempt):
|
||||||
stats[attempt.id][attempt.chart]['plays'] += 1
|
stats[attempt.id][attempt.chart]["plays"] += 1
|
||||||
if self.__is_clear(attempt):
|
if self.__is_clear(attempt):
|
||||||
stats[attempt.id][attempt.chart]['clears'] += 1
|
stats[attempt.id][attempt.chart]["clears"] += 1
|
||||||
if self.__is_combo(attempt):
|
if self.__is_combo(attempt):
|
||||||
stats[attempt.id][attempt.chart]['combos'] += 1
|
stats[attempt.id][attempt.chart]["combos"] += 1
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
for songid in stats:
|
for songid in stats:
|
||||||
for songchart in stats[songid]:
|
for songchart in stats[songid]:
|
||||||
stat = stats[songid][songchart]
|
stat = stats[songid][songchart]
|
||||||
stat['id'] = songid
|
stat["id"] = songid
|
||||||
stat['chart'] = songchart
|
stat["chart"] = songchart
|
||||||
retval.append(self.__format_statistics(stat))
|
retval.append(self.__format_statistics(stat))
|
||||||
|
|
||||||
return retval
|
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]]]] = {}
|
stats: Dict[UserID, Dict[int, Dict[int, Dict[str, int]]]] = {}
|
||||||
|
|
||||||
for (userid, attempt) in attempts:
|
for (userid, attempt) in attempts:
|
||||||
@ -157,36 +185,43 @@ class StatisticsObject(BaseObject):
|
|||||||
stats[userid][attempt.id] = {}
|
stats[userid][attempt.id] = {}
|
||||||
if attempt.chart not in stats[userid][attempt.id]:
|
if attempt.chart not in stats[userid][attempt.id]:
|
||||||
stats[userid][attempt.id][attempt.chart] = {
|
stats[userid][attempt.id][attempt.chart] = {
|
||||||
'plays': 0,
|
"plays": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'combos': 0,
|
"combos": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.__is_play(attempt):
|
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):
|
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):
|
if self.__is_combo(attempt):
|
||||||
stats[userid][attempt.id][attempt.chart]['combos'] += 1
|
stats[userid][attempt.id][attempt.chart]["combos"] += 1
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
for userid in stats:
|
for userid in stats:
|
||||||
for songid in stats[userid]:
|
for songid in stats[userid]:
|
||||||
for songchart in stats[userid][songid]:
|
for songchart in stats[userid][songid]:
|
||||||
stat = stats[userid][songid][songchart]
|
stat = stats[userid][songid][songchart]
|
||||||
stat['id'] = songid
|
stat["id"] = songid
|
||||||
stat['chart'] = songchart
|
stat["chart"] = songchart
|
||||||
retval.append(self.__format_user_statistics(cards[userid], stat))
|
retval.append(self.__format_user_statistics(cards[userid], stat))
|
||||||
|
|
||||||
return retval
|
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]] = []
|
retval: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
# Fetch the attempts
|
# Fetch the attempts
|
||||||
if idtype == APIConstants.ID_TYPE_SERVER:
|
if idtype == APIConstants.ID_TYPE_SERVER:
|
||||||
retval = self.__aggregate_global(
|
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:
|
elif idtype == APIConstants.ID_TYPE_SONG:
|
||||||
if len(ids) == 1:
|
if len(ids) == 1:
|
||||||
@ -196,7 +231,12 @@ class StatisticsObject(BaseObject):
|
|||||||
songid = int(ids[0])
|
songid = int(ids[0])
|
||||||
chart = int(ids[1])
|
chart = int(ids[1])
|
||||||
retval = self.__aggregate_global(
|
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:
|
elif idtype == APIConstants.ID_TYPE_INSTANCE:
|
||||||
songid = int(ids[0])
|
songid = int(ids[0])
|
||||||
@ -206,7 +246,13 @@ class StatisticsObject(BaseObject):
|
|||||||
if userid is not None:
|
if userid is not None:
|
||||||
retval = self.__aggregate_local(
|
retval = self.__aggregate_local(
|
||||||
{userid: self.data.local.user.get_cards(userid)},
|
{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:
|
elif idtype == APIConstants.ID_TYPE_CARD:
|
||||||
id_to_cards: Dict[int, List[str]] = {}
|
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)
|
id_to_cards[userid] = self.data.local.user.get_cards(userid)
|
||||||
attempts.extend(
|
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)
|
retval = self.__aggregate_local(id_to_cards, attempts)
|
||||||
else:
|
else:
|
||||||
raise APIException('Invalid ID type!')
|
raise APIException("Invalid ID type!")
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
@ -3,7 +3,15 @@ import traceback
|
|||||||
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Type
|
||||||
from typing_extensions import Final
|
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
|
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.
|
List of statuses we return to the game for various reasons.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUCCESS: Final[int] = 0
|
SUCCESS: Final[int] = 0
|
||||||
NO_PROFILE: Final[int] = 109
|
NO_PROFILE: Final[int] = 109
|
||||||
NOT_ALLOWED: Final[int] = 110
|
NOT_ALLOWED: Final[int] = 110
|
||||||
@ -41,7 +50,7 @@ class Factory(ABC):
|
|||||||
with Base, using Base.register(). Factories specify the game code that
|
with Base, using Base.register(). Factories specify the game code that
|
||||||
they support, which Base will use when routing requests.
|
they support, which Base will use when routing requests.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Override this in subclass!')
|
raise NotImplementedError("Override this in subclass!")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_scheduled_work(cls, data: Data, config: Config) -> None:
|
def run_scheduled_work(cls, data: Data, config: Config) -> None:
|
||||||
@ -59,10 +68,10 @@ class Factory(ABC):
|
|||||||
stack = traceback.format_exc()
|
stack = traceback.format_exc()
|
||||||
print(stack)
|
print(stack)
|
||||||
data.local.network.put_event(
|
data.local.network.put_event(
|
||||||
'exception',
|
"exception",
|
||||||
{
|
{
|
||||||
'service': 'scheduler',
|
"service": "scheduler",
|
||||||
'traceback': stack,
|
"traceback": stack,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
for event in events:
|
for event in events:
|
||||||
@ -88,7 +97,13 @@ class Factory(ABC):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@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.
|
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
|
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.
|
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):
|
class Base(ABC):
|
||||||
@ -172,7 +187,13 @@ class Base(ABC):
|
|||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
@classmethod
|
@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.
|
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)
|
return Base(data, config, model)
|
||||||
else:
|
else:
|
||||||
# Return the registered module providing this game
|
# 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
|
@classmethod
|
||||||
def register(cls, gamecode: str, handler: Type[Factory]) -> None:
|
def register(cls, gamecode: str, handler: Type[Factory]) -> None:
|
||||||
@ -216,7 +239,9 @@ class Base(ABC):
|
|||||||
cls.__registered_handlers.add(handler)
|
cls.__registered_handlers.add(handler)
|
||||||
|
|
||||||
@classmethod
|
@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.
|
Run any out-of-band scheduled work that is applicable to this game.
|
||||||
"""
|
"""
|
||||||
@ -267,7 +292,10 @@ class Base(ABC):
|
|||||||
Returns:
|
Returns:
|
||||||
True if the profile exists, False if not.
|
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]:
|
def get_profile(self, userid: UserID) -> Optional[Profile]:
|
||||||
"""
|
"""
|
||||||
@ -321,9 +349,16 @@ class Base(ABC):
|
|||||||
or an empty dictionary if nothing was found.
|
or an empty dictionary if nothing was found.
|
||||||
"""
|
"""
|
||||||
userids = list(set(userids))
|
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 [
|
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
|
for (userid, profile) in profiles
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -336,10 +371,12 @@ class Base(ABC):
|
|||||||
profile - A dictionary that should be looked up later using get_profile.
|
profile - A dictionary that should be looked up later using get_profile.
|
||||||
"""
|
"""
|
||||||
if RemoteUser.is_remote(userid):
|
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)
|
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.
|
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.
|
stats - A play statistics object we should store extra data from.
|
||||||
"""
|
"""
|
||||||
if RemoteUser.is_remote(userid):
|
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
|
# We store the play statistics in a series-wide settings blob so its available
|
||||||
# across all game versions, since it isn't game-specific.
|
# 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:
|
if stats is not None:
|
||||||
for key in stats:
|
for key in stats:
|
||||||
# Make sure we don't override anything we manage here
|
# Make sure we don't override anything we manage here
|
||||||
if key in {
|
if key in {
|
||||||
'total_plays',
|
"total_plays",
|
||||||
'today_plays',
|
"today_plays",
|
||||||
'total_days',
|
"total_days",
|
||||||
'first_play_timestamp',
|
"first_play_timestamp",
|
||||||
'last_play_timestamp',
|
"last_play_timestamp",
|
||||||
'last_play_date',
|
"last_play_date",
|
||||||
'consecutive_days',
|
"consecutive_days",
|
||||||
}:
|
}:
|
||||||
continue
|
continue
|
||||||
# Safe to copy over
|
# Safe to copy over
|
||||||
settings[key] = stats[key]
|
settings[key] = stats[key]
|
||||||
|
|
||||||
settings.replace_int('total_plays', settings.get_int('total_plays') + 1)
|
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(
|
||||||
settings.replace_int('last_play_timestamp', Time.now())
|
"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()
|
today_play_date = Time.todays_date()
|
||||||
yesterday_play_date = Time.yesterdays_date()
|
yesterday_play_date = Time.yesterdays_date()
|
||||||
if (
|
if (
|
||||||
last_play_date[0] == today_play_date[0] and
|
last_play_date[0] == today_play_date[0]
|
||||||
last_play_date[1] == today_play_date[1] and
|
and last_play_date[1] == today_play_date[1]
|
||||||
last_play_date[2] == today_play_date[2]
|
and last_play_date[2] == today_play_date[2]
|
||||||
):
|
):
|
||||||
# We already played today, add one.
|
# 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:
|
else:
|
||||||
# We played on a new day, so count total days up.
|
# 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).
|
# We played only once today (the play we are saving).
|
||||||
settings.replace_int('today_plays', 1)
|
settings.replace_int("today_plays", 1)
|
||||||
if (
|
if (
|
||||||
last_play_date[0] == yesterday_play_date[0] and
|
last_play_date[0] == yesterday_play_date[0]
|
||||||
last_play_date[1] == yesterday_play_date[1] and
|
and last_play_date[1] == yesterday_play_date[1]
|
||||||
last_play_date[2] == yesterday_play_date[2]
|
and last_play_date[2] == yesterday_play_date[2]
|
||||||
):
|
):
|
||||||
# We played yesterday, add one to consecutive days
|
# 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:
|
else:
|
||||||
# We haven't played yesterday, so we have only one consecutive day.
|
# We haven't played yesterday, so we have only one consecutive day.
|
||||||
settings.replace_int('consecutive_days', 1)
|
settings.replace_int("consecutive_days", 1)
|
||||||
settings.replace_int_array('last_play_date', 3, today_play_date)
|
settings.replace_int_array("last_play_date", 3, today_play_date)
|
||||||
|
|
||||||
# Save back
|
# Save back
|
||||||
self.data.local.game.put_settings(self.game, userid, settings)
|
self.data.local.game.put_settings(self.game, userid, settings)
|
||||||
@ -442,9 +485,13 @@ class Base(ABC):
|
|||||||
def get_machine_region(self) -> int:
|
def get_machine_region(self) -> int:
|
||||||
arcade = self.get_arcade()
|
arcade = self.get_arcade()
|
||||||
if arcade is None:
|
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:
|
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:
|
def get_game_config(self) -> ValidatedDict:
|
||||||
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
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,
|
# If this machine belongs to an arcade, use its settings. If the settings aren't present,
|
||||||
# default to the game's defaults.
|
# default to the game's defaults.
|
||||||
if machine.arcade is not None:
|
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:
|
if settings is None:
|
||||||
settings = ValidatedDict()
|
settings = ValidatedDict()
|
||||||
return settings
|
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
|
# 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.
|
# aren't present, default ot the game's default.
|
||||||
else:
|
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:
|
if settings is None:
|
||||||
settings = ValidatedDict()
|
settings = ValidatedDict()
|
||||||
return settings
|
return settings
|
||||||
@ -511,38 +565,38 @@ class Base(ABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Calculate whether we are on our first play of the day or not.
|
# 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 (
|
if (
|
||||||
last_play_date[0] == today_play_date[0] and
|
last_play_date[0] == today_play_date[0]
|
||||||
last_play_date[1] == today_play_date[1] and
|
and last_play_date[1] == today_play_date[1]
|
||||||
last_play_date[2] == today_play_date[2]
|
and last_play_date[2] == today_play_date[2]
|
||||||
):
|
):
|
||||||
# We last played today, so the total days and today plays are accurate
|
# We last played today, so the total days and today plays are accurate
|
||||||
# as stored.
|
# as stored.
|
||||||
today_count = settings.get_int('today_plays', 0)
|
today_count = settings.get_int("today_plays", 0)
|
||||||
total_days = settings.get_int('total_days', 1)
|
total_days = settings.get_int("total_days", 1)
|
||||||
consecutive_days = settings.get_int('consecutive_days', 1)
|
consecutive_days = settings.get_int("consecutive_days", 1)
|
||||||
else:
|
else:
|
||||||
if (
|
if (
|
||||||
last_play_date[0] != 0 and
|
last_play_date[0] != 0
|
||||||
last_play_date[1] != 0 and
|
and last_play_date[1] != 0
|
||||||
last_play_date[2] != 0
|
and last_play_date[2] != 0
|
||||||
):
|
):
|
||||||
# We've played before but not today, so the total days is
|
# We've played before but not today, so the total days is
|
||||||
# the stored count plus today.
|
# the stored count plus today.
|
||||||
total_days = settings.get_int('total_days') + 1
|
total_days = settings.get_int("total_days") + 1
|
||||||
else:
|
else:
|
||||||
# We've never played before, so the total days is just 1.
|
# We've never played before, so the total days is just 1.
|
||||||
total_days = 1
|
total_days = 1
|
||||||
|
|
||||||
if (
|
if (
|
||||||
last_play_date[0] == yesterday_play_date[0] and
|
last_play_date[0] == yesterday_play_date[0]
|
||||||
last_play_date[1] == yesterday_play_date[1] and
|
and last_play_date[1] == yesterday_play_date[1]
|
||||||
last_play_date[2] == yesterday_play_date[2]
|
and last_play_date[2] == yesterday_play_date[2]
|
||||||
):
|
):
|
||||||
# We've played before, and it was yesterday, so today is the
|
# We've played before, and it was yesterday, so today is the
|
||||||
# next consecutive day. So add the current value and today.
|
# 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:
|
else:
|
||||||
# This is the first consecutive day, we've either never played
|
# This is the first consecutive day, we've either never played
|
||||||
# or we played a bunch but in the past before yesterday.
|
# 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.
|
# Grab any extra settings that a game may have stored here.
|
||||||
extra_settings: Dict[str, Any] = {
|
extra_settings: Dict[str, Any] = {
|
||||||
key: value for (key, value) in settings.items()
|
key: value
|
||||||
if key not in {
|
for (key, value) in settings.items()
|
||||||
'total_plays',
|
if key
|
||||||
'today_plays',
|
not in {
|
||||||
'total_days',
|
"total_plays",
|
||||||
'first_play_timestamp',
|
"today_plays",
|
||||||
'last_play_timestamp',
|
"total_days",
|
||||||
'last_play_date',
|
"first_play_timestamp",
|
||||||
'consecutive_days',
|
"last_play_timestamp",
|
||||||
|
"last_play_date",
|
||||||
|
"consecutive_days",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PlayStatistics(
|
return PlayStatistics(
|
||||||
self.game,
|
self.game,
|
||||||
settings.get_int('total_plays') + 1,
|
settings.get_int("total_plays") + 1,
|
||||||
today_count + 1,
|
today_count + 1,
|
||||||
total_days,
|
total_days,
|
||||||
consecutive_days,
|
consecutive_days,
|
||||||
settings.get_int('first_play_timestamp', Time.now()),
|
settings.get_int("first_play_timestamp", Time.now()),
|
||||||
settings.get_int('last_play_timestamp', Time.now()),
|
settings.get_int("last_play_timestamp", Time.now()),
|
||||||
extra_settings,
|
extra_settings,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ class BishiBashiBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
game: GameConstants = GameConstants.BISHI_BASHI
|
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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# vim: set fileencoding=utf-8
|
# vim: set fileencoding=utf-8
|
||||||
import binascii
|
import binascii
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Python <= 3.9
|
# Python <= 3.9
|
||||||
from collections import Iterable
|
from collections import Iterable
|
||||||
@ -30,58 +31,58 @@ class TheStarBishiBashi(
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Force Unlock All Characters',
|
"name": "Force Unlock All Characters",
|
||||||
'tip': 'Force unlock all characters on select screen.',
|
"tip": "Force unlock all characters on select screen.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_characters',
|
"setting": "force_unlock_characters",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Unlock Non-Gacha Characters',
|
"name": "Unlock Non-Gacha Characters",
|
||||||
'tip': 'Unlock characters that require playing a different game to unlock.',
|
"tip": "Unlock characters that require playing a different game to unlock.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_eamuse_characters',
|
"setting": "force_unlock_eamuse_characters",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Enable DLC levels',
|
"name": "Enable DLC levels",
|
||||||
'tip': 'Enable extra DLC levels on newer cabinets.',
|
"tip": "Enable extra DLC levels on newer cabinets.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'enable_dlc_levels',
|
"setting": "enable_dlc_levels",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'strs': [
|
"strs": [
|
||||||
{
|
{
|
||||||
'name': 'Scrolling Announcement',
|
"name": "Scrolling Announcement",
|
||||||
'tip': 'An announcement that scrolls by in attract mode.',
|
"tip": "An announcement that scrolls by in attract mode.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'big_announcement',
|
"setting": "big_announcement",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'longstrs': [
|
"longstrs": [
|
||||||
{
|
{
|
||||||
'name': 'Bulletin Board Announcement',
|
"name": "Bulletin Board Announcement",
|
||||||
'tip': 'An announcement displayed on a bulletin board in attract mode.',
|
"tip": "An announcement displayed on a bulletin board in attract mode.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'bb_announcement',
|
"setting": "bb_announcement",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def __update_shop_name(self, profiledata: bytes) -> None:
|
def __update_shop_name(self, profiledata: bytes) -> None:
|
||||||
# Figure out the profile type
|
# Figure out the profile type
|
||||||
csvs = profiledata.split(b',')
|
csvs = profiledata.split(b",")
|
||||||
if len(csvs) < 2:
|
if len(csvs) < 2:
|
||||||
# Not long enough to care about
|
# Not long enough to care about
|
||||||
return
|
return
|
||||||
datatype = csvs[1].decode('ascii')
|
datatype = csvs[1].decode("ascii")
|
||||||
if datatype != 'IBBDAT00':
|
if datatype != "IBBDAT00":
|
||||||
# Not the right profile type requested
|
# Not the right profile type requested
|
||||||
return
|
return
|
||||||
|
|
||||||
# Grab the shop name
|
# Grab the shop name
|
||||||
try:
|
try:
|
||||||
shopname = csvs[30].decode('shift-jis')
|
shopname = csvs[30].decode("shift-jis")
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
self.update_machine_name(shopname)
|
self.update_machine_name(shopname)
|
||||||
@ -98,7 +99,9 @@ class TheStarBishiBashi(
|
|||||||
data = data.replace(";", "#;")
|
data = data.replace(";", "#;")
|
||||||
return data
|
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):
|
if isinstance(values, Iterable) and not isinstance(values, str):
|
||||||
values = ",".join(self.__escape_string(x) for x in values)
|
values = ",".join(self.__escape_string(x) for x in values)
|
||||||
else:
|
else:
|
||||||
@ -108,18 +111,18 @@ class TheStarBishiBashi(
|
|||||||
|
|
||||||
def handle_system_getmaster_request(self, request: Node) -> Node:
|
def handle_system_getmaster_request(self, request: Node) -> Node:
|
||||||
# See if we can grab the request
|
# See if we can grab the request
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
if not data:
|
if not data:
|
||||||
root = Node.void('system')
|
root = Node.void("system")
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Figure out what type of messsage this is
|
# Figure out what type of messsage this is
|
||||||
reqtype = data.child_value('datatype')
|
reqtype = data.child_value("datatype")
|
||||||
reqkey = data.child_value('datakey')
|
reqkey = data.child_value("datakey")
|
||||||
|
|
||||||
# System message
|
# System message
|
||||||
root = Node.void('system')
|
root = Node.void("system")
|
||||||
|
|
||||||
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
||||||
# Settings that we can tweak from the server.
|
# Settings that we can tweak from the server.
|
||||||
@ -179,40 +182,51 @@ class TheStarBishiBashi(
|
|||||||
settings: Dict[str, Union[int, str, Sequence[int], Sequence[str]]] = {}
|
settings: Dict[str, Union[int, str, Sequence[int], Sequence[str]]] = {}
|
||||||
|
|
||||||
game_config = self.get_game_config()
|
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:
|
if enable_dlc_levels:
|
||||||
settings['MAL'] = 1
|
settings["MAL"] = 1
|
||||||
force_unlock_characters = game_config.get_bool('force_unlock_eamuse_characters')
|
force_unlock_characters = game_config.get_bool(
|
||||||
|
"force_unlock_eamuse_characters"
|
||||||
|
)
|
||||||
if force_unlock_characters:
|
if force_unlock_characters:
|
||||||
settings['ALL'] = 1
|
settings["ALL"] = 1
|
||||||
scrolling_message = game_config.get_str('big_announcement')
|
scrolling_message = game_config.get_str("big_announcement")
|
||||||
if scrolling_message:
|
if scrolling_message:
|
||||||
settings['CM'] = scrolling_message
|
settings["CM"] = scrolling_message
|
||||||
bb_message = game_config.get_str('bb_announcement')
|
bb_message = game_config.get_str("bb_announcement")
|
||||||
if bb_message:
|
if bb_message:
|
||||||
settings['IM'] = bb_message
|
settings["IM"] = bb_message
|
||||||
|
|
||||||
# Generate system 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.
|
# 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(
|
||||||
root.add_child(Node.string('strdata2', ""))
|
Node.string(
|
||||||
root.add_child(Node.u64('updatedate', Time.now() * 1000))
|
"strdata1",
|
||||||
root.add_child(Node.s32('result', 1))
|
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:
|
else:
|
||||||
# Unknown message.
|
# Unknown message.
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||||
# Look up user by refid
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
root.add_child(
|
||||||
|
Node.s32("result", 1)
|
||||||
|
) # Unclear if this is the right thing to do here.
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Extract new profile info from old profile
|
# Extract new profile info from old profile
|
||||||
@ -227,14 +241,14 @@ class TheStarBishiBashi(
|
|||||||
self.put_profile(userid, newprofile)
|
self.put_profile(userid, newprofile)
|
||||||
|
|
||||||
# Return success!
|
# Return success!
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||||
# Look up user by refid
|
# Look up user by refid
|
||||||
refid = request.child_value('data/eaid')
|
refid = request.child_value("data/eaid")
|
||||||
profiletype = request.child_value('data/recv_csv').split(',')[0]
|
profiletype = request.child_value("data/recv_csv").split(",")[0]
|
||||||
profile = None
|
profile = None
|
||||||
userid = None
|
userid = None
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
@ -244,40 +258,44 @@ class TheStarBishiBashi(
|
|||||||
if profile is not None:
|
if profile is not None:
|
||||||
return self.format_profile(userid, profiletype, profile)
|
return self.format_profile(userid, profiletype, profile)
|
||||||
else:
|
else:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
root.add_child(
|
||||||
|
Node.s32("result", 1)
|
||||||
|
) # Unclear if this is the right thing to do here.
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profiletype: str, profile: Profile) -> Node:
|
def format_profile(
|
||||||
root = Node.void('playerdata')
|
self, userid: UserID, profiletype: str, profile: Profile
|
||||||
root.add_child(Node.s32('result', 0))
|
) -> Node:
|
||||||
player = Node.void('player')
|
root = Node.void("playerdata")
|
||||||
|
root.add_child(Node.s32("result", 0))
|
||||||
|
player = Node.void("player")
|
||||||
root.add_child(player)
|
root.add_child(player)
|
||||||
records = 0
|
records = 0
|
||||||
|
|
||||||
for i in range(len(profile['strdatas'])):
|
for i in range(len(profile["strdatas"])):
|
||||||
strdata = profile['strdatas'][i]
|
strdata = profile["strdatas"][i]
|
||||||
bindata = profile['bindatas'][i]
|
bindata = profile["bindatas"][i]
|
||||||
|
|
||||||
# Figure out the profile type
|
# Figure out the profile type
|
||||||
csvs = strdata.split(b',')
|
csvs = strdata.split(b",")
|
||||||
if len(csvs) < 2:
|
if len(csvs) < 2:
|
||||||
# Not long enough to care about
|
# Not long enough to care about
|
||||||
continue
|
continue
|
||||||
datatype = csvs[1].decode('ascii')
|
datatype = csvs[1].decode("ascii")
|
||||||
if datatype != profiletype:
|
if datatype != profiletype:
|
||||||
# Not the right profile type requested
|
# Not the right profile type requested
|
||||||
continue
|
continue
|
||||||
|
|
||||||
game_config = self.get_game_config()
|
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:
|
if force_unlock_characters:
|
||||||
csvs[11] = b'3ffffffffffff'
|
csvs[11] = b"3ffffffffffff"
|
||||||
else:
|
else:
|
||||||
# Reward characters based on playing other games on the network
|
# 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:
|
while (len(hexdata) & 1) != 0:
|
||||||
hexdata = '0' + hexdata
|
hexdata = "0" + hexdata
|
||||||
unlock_bits = [b for b in binascii.unhexlify(hexdata)]
|
unlock_bits = [b for b in binascii.unhexlify(hexdata)]
|
||||||
while len(unlock_bits) < 7:
|
while len(unlock_bits) < 7:
|
||||||
unlock_bits.insert(0, 0)
|
unlock_bits.insert(0, 0)
|
||||||
@ -309,32 +327,34 @@ class TheStarBishiBashi(
|
|||||||
|
|
||||||
# Reconstruct table
|
# Reconstruct table
|
||||||
unlock_bits = unlock_bits[::-1]
|
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
|
# This is a valid profile node for this type, lets return only the profile values
|
||||||
strdata = b','.join(csvs[2:])
|
strdata = b",".join(csvs[2:])
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
player.add_child(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)
|
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
|
# Remember that we had this record
|
||||||
records = records + 1
|
records = records + 1
|
||||||
|
|
||||||
player.add_child(Node.u32('record_num', records))
|
player.add_child(Node.u32("record_num", records))
|
||||||
return root
|
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.
|
# Profile save request, data values are base64 encoded.
|
||||||
# d is a CSV, and bin1 is binary data.
|
# d is a CSV, and bin1 is binary data.
|
||||||
newprofile = oldprofile.clone()
|
newprofile = oldprofile.clone()
|
||||||
strdatas: List[bytes] = []
|
strdatas: List[bytes] = []
|
||||||
bindatas: List[bytes] = []
|
bindatas: List[bytes] = []
|
||||||
|
|
||||||
record = request.child('data/record')
|
record = request.child("data/record")
|
||||||
for node in record.children:
|
for node in record.children:
|
||||||
if node.name != 'd':
|
if node.name != "d":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
profile = base64.b64decode(node.value)
|
profile = base64.b64decode(node.value)
|
||||||
@ -344,10 +364,10 @@ class TheStarBishiBashi(
|
|||||||
if is_new:
|
if is_new:
|
||||||
self.__update_shop_name(profile)
|
self.__update_shop_name(profile)
|
||||||
strdatas.append(profile)
|
strdatas.append(profile)
|
||||||
bindatas.append(base64.b64decode(node.child_value('bin1')))
|
bindatas.append(base64.b64decode(node.child_value("bin1")))
|
||||||
|
|
||||||
newprofile['strdatas'] = strdatas
|
newprofile["strdatas"] = strdatas
|
||||||
newprofile['bindatas'] = bindatas
|
newprofile["bindatas"] = bindatas
|
||||||
|
|
||||||
# Keep track of play statistics across all versions
|
# Keep track of play statistics across all versions
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
@ -14,13 +14,19 @@ class BishiBashiFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['IBB']:
|
for gamecode in ["IBB"]:
|
||||||
Base.register(gamecode, BishiBashiFactory)
|
Base.register(gamecode, BishiBashiFactory)
|
||||||
|
|
||||||
@classmethod
|
@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)
|
return TheStarBishiBashi(data, config, model)
|
||||||
|
|
||||||
# Unknown game version
|
# Unknown game version
|
||||||
|
@ -19,14 +19,14 @@ class CardManagerHandler(Base):
|
|||||||
# Given a cardid, look up the dataid/refid (same thing in this system).
|
# 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
|
# If the card doesn't exist or isn't allowed, return a status specifying this
|
||||||
# instead of the results of the dataid/refid lookup.
|
# instead of the results of the dataid/refid lookup.
|
||||||
cardid = request.attribute('cardid')
|
cardid = request.attribute("cardid")
|
||||||
modelstring = request.attribute('model')
|
modelstring = request.attribute("model")
|
||||||
userid = self.data.local.user.from_cardid(cardid)
|
userid = self.data.local.user.from_cardid(cardid)
|
||||||
|
|
||||||
if userid is None:
|
if userid is None:
|
||||||
# This user doesn't exist, force system to create new account
|
# This user doesn't exist, force system to create new account
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('status', str(Status.NOT_REGISTERED))
|
root.set_attribute("status", str(Status.NOT_REGISTERED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Special handling for looking up whether the previous game's profile existed. If we
|
# 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)
|
refid = self.data.local.user.get_refid(self.game, self.version, userid)
|
||||||
paseli_enabled = self.supports_paseli and self.config.paseli.enabled
|
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 = Node.void("cardmng")
|
||||||
root.set_attribute('refid', refid)
|
root.set_attribute("refid", refid)
|
||||||
root.set_attribute('dataid', refid)
|
root.set_attribute("dataid", refid)
|
||||||
|
|
||||||
# Unsure what this does, but it appears not to matter so we set it to my best guess.
|
# 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
|
# 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.
|
# 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
|
# 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.
|
# 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.
|
# 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.
|
# I have absolutely no idea what these do.
|
||||||
root.set_attribute('useridflag', '1')
|
root.set_attribute("useridflag", "1")
|
||||||
root.set_attribute('extidflag', '1')
|
root.set_attribute("extidflag", "1")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_cardmng_authpass_request(self, request: Node) -> Node:
|
def handle_cardmng_authpass_request(self, request: Node) -> Node:
|
||||||
# Given a dataid/refid previously found via inquire, verify the pin
|
# Given a dataid/refid previously found via inquire, verify the pin
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
pin = request.attribute('pass')
|
pin = request.attribute("pass")
|
||||||
userid = self.data.local.user.from_refid(self.game, self.version, refid)
|
userid = self.data.local.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
valid = self.data.local.user.validate_pin(userid, pin)
|
valid = self.data.local.user.validate_pin(userid, pin)
|
||||||
else:
|
else:
|
||||||
valid = False
|
valid = False
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('status', str(Status.SUCCESS if valid else Status.INVALID_PIN))
|
root.set_attribute(
|
||||||
|
"status", str(Status.SUCCESS if valid else Status.INVALID_PIN)
|
||||||
|
)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_cardmng_getrefid_request(self, request: Node) -> Node:
|
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
|
# Given a cardid and a pin, register the card with the system and generate a new dataid/refid + extid
|
||||||
cardid = request.attribute('cardid')
|
cardid = request.attribute("cardid")
|
||||||
pin = request.attribute('passwd')
|
pin = request.attribute("passwd")
|
||||||
userid = self.data.local.user.create_account(cardid, pin)
|
userid = self.data.local.user.create_account(cardid, pin)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
# This user can't be created
|
# This user can't be created
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
refid = self.data.local.user.create_refid(self.game, self.version, userid)
|
refid = self.data.local.user.create_refid(self.game, self.version, userid)
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('dataid', refid)
|
root.set_attribute("dataid", refid)
|
||||||
root.set_attribute('refid', refid)
|
root.set_attribute("refid", refid)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_cardmng_bindmodel_request(self, request: Node) -> Node:
|
def handle_cardmng_bindmodel_request(self, request: Node) -> Node:
|
||||||
# Given a refid, bind the user's card to the current version of the game
|
# 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)
|
userid = self.data.local.user.from_refid(self.game, self.version, refid)
|
||||||
self.bind_profile(userid)
|
self.bind_profile(userid)
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('dataid', refid)
|
root.set_attribute("dataid", refid)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_cardmng_getkeepspan_request(self, request: Node) -> Node:
|
def handle_cardmng_getkeepspan_request(self, request: Node) -> Node:
|
||||||
# Unclear what this method does, return an arbitrary span
|
# Unclear what this method does, return an arbitrary span
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
root.set_attribute('keepspan', '30')
|
root.set_attribute("keepspan", "30")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_cardmng_getdatalist_request(self, request: Node) -> Node:
|
def handle_cardmng_getdatalist_request(self, request: Node) -> Node:
|
||||||
# Unclear what this method does, return a dummy response
|
# Unclear what this method does, return a dummy response
|
||||||
root = Node.void('cardmng')
|
root = Node.void("cardmng")
|
||||||
return root
|
return root
|
||||||
|
@ -16,42 +16,45 @@ class CoreHandler(Base):
|
|||||||
each server which handles a particular service. For us, this is always
|
each server which handles a particular service. For us, this is always
|
||||||
our URL since we serve everything.
|
our URL since we serve everything.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def item(name: str, url: str) -> Node:
|
def item(name: str, url: str) -> Node:
|
||||||
node = Node.void('item')
|
node = Node.void("item")
|
||||||
node.set_attribute('name', name)
|
node.set_attribute("name", name)
|
||||||
node.set_attribute('url', url)
|
node.set_attribute("url", url)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
url = f'{"https" if self.config.server.https else "http"}://{self.config.server.address}:{self.config.server.port}/'
|
url = f'{"https" if self.config.server.https else "http"}://{self.config.server.address}:{self.config.server.port}/'
|
||||||
root = Node.void('services')
|
root = Node.void("services")
|
||||||
root.set_attribute('expire', '600')
|
root.set_attribute("expire", "600")
|
||||||
# This can be set to 'operation', 'debug', 'test', and 'factory'.
|
# This can be set to 'operation', 'debug', 'test', and 'factory'.
|
||||||
root.set_attribute('mode', 'operation')
|
root.set_attribute("mode", "operation")
|
||||||
root.set_attribute('product_domain', '1')
|
root.set_attribute("product_domain", "1")
|
||||||
|
|
||||||
root.add_child(item('cardmng', url))
|
root.add_child(item("cardmng", url))
|
||||||
root.add_child(item('dlstatus', url))
|
root.add_child(item("dlstatus", url))
|
||||||
root.add_child(item('eacoin', url))
|
root.add_child(item("eacoin", url))
|
||||||
root.add_child(item('facility', url))
|
root.add_child(item("facility", url))
|
||||||
root.add_child(item('lobby', url))
|
root.add_child(item("lobby", url))
|
||||||
root.add_child(item('local', url))
|
root.add_child(item("local", url))
|
||||||
root.add_child(item('message', url))
|
root.add_child(item("message", url))
|
||||||
root.add_child(item('package', url))
|
root.add_child(item("package", url))
|
||||||
root.add_child(item('pcbevent', url))
|
root.add_child(item("pcbevent", url))
|
||||||
root.add_child(item('pcbtracker', url))
|
root.add_child(item("pcbtracker", url))
|
||||||
root.add_child(item('pkglist', url))
|
root.add_child(item("pkglist", url))
|
||||||
root.add_child(item('posevent', url))
|
root.add_child(item("posevent", url))
|
||||||
for srv in self.extra_services:
|
for srv in self.extra_services:
|
||||||
root.add_child(item(srv, url))
|
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
|
# Translate keepalive to a raw IP because we can't give out a host here
|
||||||
keepalive = socket.gethostbyname(self.config.server.keepalive)
|
keepalive = socket.gethostbyname(self.config.server.keepalive)
|
||||||
root.add_child(item(
|
root.add_child(
|
||||||
'keepalive',
|
item(
|
||||||
f'http://{keepalive}/core/keepalive?pa={keepalive}&ia={keepalive}&ga={keepalive}&ma={keepalive}&t1=2&t2=10',
|
"keepalive",
|
||||||
))
|
f"http://{keepalive}/core/keepalive?pa={keepalive}&ia={keepalive}&ga={keepalive}&ma={keepalive}&t1=2&t2=10",
|
||||||
|
)
|
||||||
|
)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_pcbtracker_alive_request(self, request: Node) -> Node:
|
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.
|
which returns whether PASELI should be active or not for this session.
|
||||||
"""
|
"""
|
||||||
# Reports that a machine is booting. Overloaded to enable/disable paseli
|
# Reports that a machine is booting. Overloaded to enable/disable paseli
|
||||||
root = Node.void('pcbtracker')
|
root = Node.void("pcbtracker")
|
||||||
root.set_attribute('ecenable', '1' if (self.supports_paseli and self.config.paseli.enabled) else '0')
|
root.set_attribute(
|
||||||
root.set_attribute('expire', '600')
|
"ecenable",
|
||||||
|
"1" if (self.supports_paseli and self.config.paseli.enabled) else "0",
|
||||||
|
)
|
||||||
|
root.set_attribute("expire", "600")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_pcbevent_put_request(self, request: Node) -> Node:
|
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.
|
Handle a PCBEvent request. We do nothing for this aside from logging the event.
|
||||||
"""
|
"""
|
||||||
for item in request.children:
|
for item in request.children:
|
||||||
if item.name == 'item':
|
if item.name == "item":
|
||||||
name = item.child_value('name')
|
name = item.child_value("name")
|
||||||
value = item.child_value('value')
|
value = item.child_value("value")
|
||||||
timestamp = item.child_value('time')
|
timestamp = item.child_value("time")
|
||||||
self.data.local.network.put_event(
|
self.data.local.network.put_event(
|
||||||
'pcbevent',
|
"pcbevent",
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
'value': value,
|
"value": value,
|
||||||
'model': str(self.model),
|
"model": str(self.model),
|
||||||
'pcbid': self.config.machine.pcbid,
|
"pcbid": self.config.machine.pcbid,
|
||||||
'ip': self.config.client.address,
|
"ip": self.config.client.address,
|
||||||
},
|
},
|
||||||
timestamp=timestamp,
|
timestamp=timestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
return Node.void('pcbevent')
|
return Node.void("pcbevent")
|
||||||
|
|
||||||
def handle_package_list_request(self, request: Node) -> Node:
|
def handle_package_list_request(self, request: Node) -> Node:
|
||||||
"""
|
"""
|
||||||
@ -94,8 +100,8 @@ class CoreHandler(Base):
|
|||||||
We don't support this at the moment.
|
We don't support this at the moment.
|
||||||
"""
|
"""
|
||||||
# List all available update packages on the server
|
# List all available update packages on the server
|
||||||
root = Node.void('package')
|
root = Node.void("package")
|
||||||
root.set_attribute('expire', '600')
|
root.set_attribute("expire", "600")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_message_get_request(self, request: Node) -> Node:
|
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
|
I have absolutely no fucking idea what this does, but it might be for
|
||||||
operator messages?
|
operator messages?
|
||||||
"""
|
"""
|
||||||
root = Node.void('message')
|
root = Node.void("message")
|
||||||
root.set_attribute('expire', '600')
|
root.set_attribute("expire", "600")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_dlstatus_progress_request(self, request: Node) -> Node:
|
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
|
I have absolutely no fucking idea what this does either, download
|
||||||
status reports maybe?
|
status reports maybe?
|
||||||
"""
|
"""
|
||||||
return Node.void('dlstatus')
|
return Node.void("dlstatus")
|
||||||
|
|
||||||
def handle_facility_get_request(self, request: Node) -> Node:
|
def handle_facility_get_request(self, request: Node) -> Node:
|
||||||
"""
|
"""
|
||||||
@ -166,45 +172,57 @@ class CoreHandler(Base):
|
|||||||
country = "XX"
|
country = "XX"
|
||||||
regionstr = ""
|
regionstr = ""
|
||||||
|
|
||||||
root = Node.void('facility')
|
root = Node.void("facility")
|
||||||
root.set_attribute('expire', '600')
|
root.set_attribute("expire", "600")
|
||||||
location = Node.void('location')
|
location = Node.void("location")
|
||||||
location.add_child(Node.string('id', ID.format_machine_id(machine.id, region=country)))
|
location.add_child(
|
||||||
location.add_child(Node.string('country', country))
|
Node.string("id", ID.format_machine_id(machine.id, region=country))
|
||||||
location.add_child(Node.string('region', regionstr))
|
)
|
||||||
location.add_child(Node.string('name', machine.name))
|
location.add_child(Node.string("country", country))
|
||||||
location.add_child(Node.u8('type', 0))
|
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 = Node.void("line")
|
||||||
line.add_child(Node.string('id', '.'))
|
line.add_child(Node.string("id", "."))
|
||||||
line.add_child(Node.u8('class', 0))
|
line.add_child(Node.u8("class", 0))
|
||||||
|
|
||||||
portfw = Node.void('portfw')
|
portfw = Node.void("portfw")
|
||||||
portfw.add_child(Node.ipv4('globalip', self.config.client.address))
|
portfw.add_child(Node.ipv4("globalip", self.config.client.address))
|
||||||
portfw.add_child(Node.u16('globalport', machine.port))
|
portfw.add_child(Node.u16("globalport", machine.port))
|
||||||
portfw.add_child(Node.u16('privateport', machine.port))
|
portfw.add_child(Node.u16("privateport", machine.port))
|
||||||
|
|
||||||
public = Node.void('public')
|
public = Node.void("public")
|
||||||
public.add_child(Node.u8('flag', 1))
|
public.add_child(Node.u8("flag", 1))
|
||||||
public.add_child(Node.string('name', '.'))
|
public.add_child(Node.string("name", "."))
|
||||||
public.add_child(Node.string('latitude', '0'))
|
public.add_child(Node.string("latitude", "0"))
|
||||||
public.add_child(Node.string('longitude', '0'))
|
public.add_child(Node.string("longitude", "0"))
|
||||||
|
|
||||||
share = Node.void('share')
|
share = Node.void("share")
|
||||||
eacoin = Node.void('eacoin')
|
eacoin = Node.void("eacoin")
|
||||||
eacoin.add_child(Node.s32('notchamount', 3000))
|
eacoin.add_child(Node.s32("notchamount", 3000))
|
||||||
eacoin.add_child(Node.s32('notchcount', 3))
|
eacoin.add_child(Node.s32("notchcount", 3))
|
||||||
eacoin.add_child(Node.s32('supplylimit', 10000))
|
eacoin.add_child(Node.s32("supplylimit", 10000))
|
||||||
|
|
||||||
eapass = Node.void('eapass')
|
eapass = Node.void("eapass")
|
||||||
eapass.add_child(Node.u16('valid', 365))
|
eapass.add_child(Node.u16("valid", 365))
|
||||||
|
|
||||||
url = Node.void('url')
|
url = Node.void("url")
|
||||||
url.add_child(Node.string('eapass', self.config.server.uri or 'www.ea-pass.konami.net'))
|
url.add_child(
|
||||||
url.add_child(Node.string('arcadefan', self.config.server.uri or 'www.konami.jp/am'))
|
Node.string("eapass", self.config.server.uri or "www.ea-pass.konami.net")
|
||||||
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(
|
||||||
url.add_child(Node.string('eagate', self.config.server.uri or 'http://eagate.573.jp'))
|
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(eacoin)
|
||||||
share.add_child(url)
|
share.add_child(url)
|
||||||
|
@ -27,32 +27,32 @@ class PASELIHandler(Base):
|
|||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
cardid = request.child_value('cardid')
|
cardid = request.child_value("cardid")
|
||||||
pin = request.child_value('passwd')
|
pin = request.child_value("passwd")
|
||||||
|
|
||||||
if cardid is None or pin is None:
|
if cardid is None or pin is None:
|
||||||
# Refuse to return anything
|
# Refuse to return anything
|
||||||
print("Invalid eacoin checkin request, missing cardid or pin")
|
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
|
return root
|
||||||
|
|
||||||
userid = self.data.local.user.from_cardid(cardid)
|
userid = self.data.local.user.from_cardid(cardid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("No user for eacoin checkin request")
|
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
|
return root
|
||||||
|
|
||||||
valid = self.data.local.user.validate_pin(userid, pin)
|
valid = self.data.local.user.validate_pin(userid, pin)
|
||||||
if not valid:
|
if not valid:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("User entered invalid pin for eacoin checkin request")
|
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
|
return root
|
||||||
|
|
||||||
session = self.data.local.user.create_session(userid)
|
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.
|
# enabled, so there's no way to find a balance.
|
||||||
balance = 0
|
balance = 0
|
||||||
else:
|
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.s16("sequence", 0))
|
||||||
root.add_child(Node.u8('acstatus', 0))
|
root.add_child(Node.u8("acstatus", 0))
|
||||||
root.add_child(Node.string('acid', 'DUMMY_ID'))
|
root.add_child(Node.string("acid", "DUMMY_ID"))
|
||||||
root.add_child(Node.string('acname', 'DUMMY_NAME'))
|
root.add_child(Node.string("acname", "DUMMY_NAME"))
|
||||||
root.add_child(Node.s32('balance', balance))
|
root.add_child(Node.s32("balance", balance))
|
||||||
root.add_child(Node.string('sessid', session))
|
root.add_child(Node.string("sessid", session))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_eacoin_opcheckin_request(self, request: Node) -> Node:
|
def handle_eacoin_opcheckin_request(self, request: Node) -> Node:
|
||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
passwd = request.child_value('passwd')
|
passwd = request.child_value("passwd")
|
||||||
|
|
||||||
if passwd is None:
|
if passwd is None:
|
||||||
# Refuse to return anything
|
# Refuse to return anything
|
||||||
print("Invalid eacoin checkin request, missing passwd")
|
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
|
return root
|
||||||
|
|
||||||
if self.config.machine.arcade is None:
|
if self.config.machine.arcade is None:
|
||||||
# Machine doesn't belong to an arcade
|
# Machine doesn't belong to an arcade
|
||||||
print("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
|
return root
|
||||||
|
|
||||||
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
||||||
if arcade is None:
|
if arcade is None:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("No arcade for operator checkin request")
|
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
|
return root
|
||||||
|
|
||||||
if arcade.pin != passwd:
|
if arcade.pin != passwd:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("User entered invalid pin for operator checkin request")
|
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
|
return root
|
||||||
|
|
||||||
session = self.data.local.machine.create_session(arcade.id)
|
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
|
return root
|
||||||
|
|
||||||
def handle_eacoin_consume_request(self, request: Node) -> Node:
|
def handle_eacoin_consume_request(self, request: Node) -> Node:
|
||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def make_resp(status: int, balance: int) -> Node:
|
def make_resp(status: int, balance: int) -> Node:
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.add_child(Node.u8('acstatus', status))
|
root.add_child(Node.u8("acstatus", status))
|
||||||
root.add_child(Node.u8('autocharge', 0))
|
root.add_child(Node.u8("autocharge", 0))
|
||||||
root.add_child(Node.s32('balance', balance))
|
root.add_child(Node.s32("balance", balance))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
session = request.child_value('sessid')
|
session = request.child_value("sessid")
|
||||||
payment = request.child_value('payment')
|
payment = request.child_value("payment")
|
||||||
service = request.child_value('service')
|
service = request.child_value("service")
|
||||||
details = request.child_value('detail')
|
details = request.child_value("detail")
|
||||||
if session is None or payment is None:
|
if session is None or payment is None:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("Invalid eacoin consume request, missing sessid or payment")
|
print("Invalid eacoin consume request, missing sessid or payment")
|
||||||
@ -156,20 +158,27 @@ class PASELIHandler(Base):
|
|||||||
else:
|
else:
|
||||||
# Look up the new balance based on this delta. If there isn't enough,
|
# 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.
|
# 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:
|
if balance is None:
|
||||||
print("Not enough balance for eacoin consume request")
|
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:
|
else:
|
||||||
self.data.local.network.put_event(
|
self.data.local.network.put_event(
|
||||||
'paseli_transaction',
|
"paseli_transaction",
|
||||||
{
|
{
|
||||||
'delta': -payment,
|
"delta": -payment,
|
||||||
'balance': balance,
|
"balance": balance,
|
||||||
'service': -service,
|
"service": -service,
|
||||||
'reason': details,
|
"reason": details,
|
||||||
'pcbid': self.config.machine.pcbid,
|
"pcbid": self.config.machine.pcbid,
|
||||||
},
|
},
|
||||||
userid=userid,
|
userid=userid,
|
||||||
arcadeid=self.config.machine.arcade,
|
arcadeid=self.config.machine.arcade,
|
||||||
@ -181,16 +190,16 @@ class PASELIHandler(Base):
|
|||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
sessid = request.child_value('sessid')
|
sessid = request.child_value("sessid")
|
||||||
logtype = request.child_value('logtype')
|
logtype = request.child_value("logtype")
|
||||||
target = request.child_value('target')
|
target = request.child_value("target")
|
||||||
limit = request.child_value('perpage')
|
limit = request.child_value("perpage")
|
||||||
offset = request.child_value('offset')
|
offset = request.child_value("offset")
|
||||||
|
|
||||||
# Try to determine whether its a user or an arcade session
|
# Try to determine whether its a user or an arcade session
|
||||||
userid = self.data.local.user.from_session(sessid)
|
userid = self.data.local.user.from_session(sessid)
|
||||||
@ -217,47 +226,59 @@ class PASELIHandler(Base):
|
|||||||
events = self.data.local.network.get_events(
|
events = self.data.local.network.get_events(
|
||||||
userid=userid,
|
userid=userid,
|
||||||
arcadeid=arcadeid,
|
arcadeid=arcadeid,
|
||||||
event='paseli_transaction',
|
event="paseli_transaction",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Further filter it down to the current PCBID
|
# 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
|
# Grab the end of day today as a timestamp
|
||||||
end_of_today = Time.end_of_today()
|
end_of_today = Time.end_of_today()
|
||||||
time_format = '%Y-%m-%d %H:%M:%S'
|
time_format = "%Y-%m-%d %H:%M:%S"
|
||||||
date_format = '%Y-%m-%d'
|
date_format = "%Y-%m-%d"
|
||||||
|
|
||||||
# Set up common structure
|
# Set up common structure
|
||||||
lognode = Node.void(logtype)
|
lognode = Node.void(logtype)
|
||||||
topic = Node.void('topic')
|
topic = Node.void("topic")
|
||||||
lognode.add_child(topic)
|
lognode.add_child(topic)
|
||||||
summary = Node.void('summary')
|
summary = Node.void("summary")
|
||||||
lognode.add_child(summary)
|
lognode.add_child(summary)
|
||||||
|
|
||||||
# Display what day we are summed to
|
# 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
|
# 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
|
beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
|
||||||
end_of_week = beginning_of_today
|
end_of_week = beginning_of_today
|
||||||
beginning_of_week = end_of_week - Time.SECONDS_IN_WEEK
|
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(
|
||||||
topic.add_child(Node.string('sumto', Time.format(end_of_week, date_format)))
|
Node.string("sumfrom", Time.format(beginning_of_week, date_format))
|
||||||
today_total = sum([
|
)
|
||||||
-event.data.get_int('delta') for event in events
|
topic.add_child(Node.string("sumto", Time.format(end_of_week, date_format)))
|
||||||
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([
|
today_total = sum(
|
||||||
-event.data.get_int('delta') for event in events
|
[
|
||||||
if event.timestamp >= beginning_of_today and event.timestamp < end_of_today
|
-event.data.get_int("delta")
|
||||||
])
|
for event in events
|
||||||
|
if event.timestamp >= beginning_of_today
|
||||||
|
and event.timestamp < end_of_today
|
||||||
|
]
|
||||||
|
)
|
||||||
week_txns = [
|
week_txns = [
|
||||||
-event.data.get_int('delta') for event in events
|
-event.data.get_int("delta")
|
||||||
if event.timestamp >= beginning_of_week and event.timestamp < end_of_week
|
for event in events
|
||||||
|
if event.timestamp >= beginning_of_week
|
||||||
|
and event.timestamp < end_of_week
|
||||||
]
|
]
|
||||||
week_total = sum(week_txns)
|
week_total = sum(week_txns)
|
||||||
if len(week_txns) > 0:
|
if len(week_txns) > 0:
|
||||||
@ -272,23 +293,38 @@ class PASELIHandler(Base):
|
|||||||
end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY)
|
end_of_day = end_of_week - (days * Time.SECONDS_IN_DAY)
|
||||||
start_of_day = end_of_day - Time.SECONDS_IN_DAY
|
start_of_day = end_of_day - Time.SECONDS_IN_DAY
|
||||||
|
|
||||||
items.append(sum([
|
items.append(
|
||||||
-event.data.get_int('delta') for event in events
|
sum(
|
||||||
if event.timestamp >= start_of_day and event.timestamp < end_of_day
|
[
|
||||||
]))
|
-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("today", today_total))
|
||||||
topic.add_child(Node.s32('average', week_avg))
|
topic.add_child(Node.s32("average", week_avg))
|
||||||
topic.add_child(Node.s32('total', week_total))
|
topic.add_child(Node.s32("total", week_total))
|
||||||
summary.add_child(Node.s32_array('items', items))
|
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.
|
# Start one week back, since the operator can look at last7days for newer stuff.
|
||||||
beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
|
beginning_of_today = end_of_today - Time.SECONDS_IN_DAY
|
||||||
end_of_52_weeks = beginning_of_today - Time.SECONDS_IN_WEEK
|
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(
|
||||||
topic.add_child(Node.string('sumto', Time.format(end_of_52_weeks, date_format)))
|
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...
|
# We index backwards, where index 0 = the first week back, 1 = the next week back after that, etc...
|
||||||
items = []
|
items = []
|
||||||
@ -296,45 +332,53 @@ class PASELIHandler(Base):
|
|||||||
end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK)
|
end_of_range = end_of_52_weeks - (weeks * Time.SECONDS_IN_WEEK)
|
||||||
beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK
|
beginning_of_range = end_of_range - Time.SECONDS_IN_WEEK
|
||||||
|
|
||||||
items.append(sum([
|
items.append(
|
||||||
-event.data.get_int('delta') for event in events
|
sum(
|
||||||
if event.timestamp >= beginning_of_range and event.timestamp < end_of_range
|
[
|
||||||
]))
|
-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()
|
start_ts = Time.now()
|
||||||
end_ts = Time.now()
|
end_ts = Time.now()
|
||||||
weekdays = [0] * 7
|
weekdays = [0] * 7
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
event_day = Time.days_into_week(event.timestamp)
|
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:
|
if event.timestamp < start_ts:
|
||||||
start_ts = event.timestamp
|
start_ts = event.timestamp
|
||||||
|
|
||||||
topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
|
topic.add_child(Node.string("sumfrom", Time.format(start_ts, date_format)))
|
||||||
topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
|
topic.add_child(Node.string("sumto", Time.format(end_ts, date_format)))
|
||||||
summary.add_child(Node.s32_array('items', weekdays))
|
summary.add_child(Node.s32_array("items", weekdays))
|
||||||
|
|
||||||
if logtype == 'eachhour':
|
if logtype == "eachhour":
|
||||||
start_ts = Time.now()
|
start_ts = Time.now()
|
||||||
end_ts = Time.now()
|
end_ts = Time.now()
|
||||||
hours = [0] * 24
|
hours = [0] * 24
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
event_hour = int((event.timestamp % Time.SECONDS_IN_DAY) / Time.SECONDS_IN_HOUR)
|
event_hour = int(
|
||||||
hours[event_hour] = hours[event_hour] - event.data.get_int('delta')
|
(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:
|
if event.timestamp < start_ts:
|
||||||
start_ts = event.timestamp
|
start_ts = event.timestamp
|
||||||
|
|
||||||
topic.add_child(Node.string('sumfrom', Time.format(start_ts, date_format)))
|
topic.add_child(Node.string("sumfrom", Time.format(start_ts, date_format)))
|
||||||
topic.add_child(Node.string('sumto', Time.format(end_ts, date_format)))
|
topic.add_child(Node.string("sumto", Time.format(end_ts, date_format)))
|
||||||
summary.add_child(Node.s32_array('items', hours))
|
summary.add_child(Node.s32_array("items", hours))
|
||||||
|
|
||||||
if logtype == 'detail':
|
if logtype == "detail":
|
||||||
history = Node.void('history')
|
history = Node.void("history")
|
||||||
lognode.add_child(history)
|
lognode.add_child(history)
|
||||||
|
|
||||||
# Respect details paging
|
# Respect details paging
|
||||||
@ -345,7 +389,7 @@ class PASELIHandler(Base):
|
|||||||
|
|
||||||
# Output the details themselves
|
# Output the details themselves
|
||||||
for event in events:
|
for event in events:
|
||||||
card_no = ''
|
card_no = ""
|
||||||
if event.userid is not None:
|
if event.userid is not None:
|
||||||
user = self.data.local.user.get_user(event.userid)
|
user = self.data.local.user.get_user(event.userid)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
@ -353,24 +397,30 @@ class PASELIHandler(Base):
|
|||||||
if len(cards) > 0:
|
if len(cards) > 0:
|
||||||
card_no = CardCipher.encode(cards[0])
|
card_no = CardCipher.encode(cards[0])
|
||||||
|
|
||||||
item = Node.void('item')
|
item = Node.void("item")
|
||||||
history.add_child(item)
|
history.add_child(item)
|
||||||
item.add_child(Node.string('date', Time.format(event.timestamp, time_format)))
|
item.add_child(
|
||||||
item.add_child(Node.s32('consume', -event.data.get_int('delta')))
|
Node.string("date", Time.format(event.timestamp, time_format))
|
||||||
item.add_child(Node.s32('service', -event.data.get_int('service')))
|
)
|
||||||
item.add_child(Node.string('cardtype', ''))
|
item.add_child(Node.s32("consume", -event.data.get_int("delta")))
|
||||||
item.add_child(Node.string('cardno', ' ' * self.paseli_padding + card_no))
|
item.add_child(Node.s32("service", -event.data.get_int("service")))
|
||||||
item.add_child(Node.string('title', ''))
|
item.add_child(Node.string("cardtype", ""))
|
||||||
item.add_child(Node.string('systemid', ''))
|
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()
|
year, month, _ = Time.todays_date()
|
||||||
this_month = Time.timestamp_from_date(year, month)
|
this_month = Time.timestamp_from_date(year, month)
|
||||||
last_month = Time.timestamp_from_date(year, month - 1)
|
last_month = Time.timestamp_from_date(year, month - 1)
|
||||||
month_before = Time.timestamp_from_date(year, month - 2)
|
month_before = Time.timestamp_from_date(year, month - 2)
|
||||||
|
|
||||||
topic.add_child(Node.string('sumfrom', Time.format(month_before, date_format)))
|
topic.add_child(
|
||||||
topic.add_child(Node.string('sumto', Time.format(this_month, date_format)))
|
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)]:
|
for (start, end) in [(month_before, last_month), (last_month, this_month)]:
|
||||||
year, month, _ = Time.date_from_timestamp(start)
|
year, month, _ = Time.date_from_timestamp(start)
|
||||||
@ -384,18 +434,24 @@ class PASELIHandler(Base):
|
|||||||
items.append(0)
|
items.append(0)
|
||||||
else:
|
else:
|
||||||
# Sum up all the txns for this day
|
# Sum up all the txns for this day
|
||||||
items.append(sum([
|
items.append(
|
||||||
-event.data.get_int('delta') for event in events
|
sum(
|
||||||
if event.timestamp >= begin_ts and event.timestamp < end_ts
|
[
|
||||||
]))
|
-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)
|
summary.add_child(item)
|
||||||
item.add_child(Node.s32('year', year))
|
item.add_child(Node.s32("year", year))
|
||||||
item.add_child(Node.s32('month', month))
|
item.add_child(Node.s32("month", month))
|
||||||
item.add_child(Node.s32_array('items', items))
|
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)
|
root.add_child(lognode)
|
||||||
return root
|
return root
|
||||||
|
|
||||||
@ -403,37 +459,37 @@ class PASELIHandler(Base):
|
|||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
oldpass = request.child_value('passwd')
|
oldpass = request.child_value("passwd")
|
||||||
newpass = request.child_value('newpasswd')
|
newpass = request.child_value("newpasswd")
|
||||||
|
|
||||||
if oldpass is None or newpass is None:
|
if oldpass is None or newpass is None:
|
||||||
# Refuse to return anything
|
# Refuse to return anything
|
||||||
print("Invalid eacoin pass change request, missing passwd")
|
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
|
return root
|
||||||
|
|
||||||
if self.config.machine.arcade is None:
|
if self.config.machine.arcade is None:
|
||||||
# Machine doesn't belong to an arcade
|
# Machine doesn't belong to an arcade
|
||||||
print("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
|
return root
|
||||||
|
|
||||||
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
arcade = self.data.local.machine.get_arcade(self.config.machine.arcade)
|
||||||
if arcade is None:
|
if arcade is None:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("No arcade for operator pass change request")
|
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
|
return root
|
||||||
|
|
||||||
if arcade.pin != oldpass:
|
if arcade.pin != oldpass:
|
||||||
# Refuse to do anything
|
# Refuse to do anything
|
||||||
print("User entered invalid pin for operator pass change request")
|
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
|
return root
|
||||||
|
|
||||||
arcade.pin = newpass
|
arcade.pin = newpass
|
||||||
@ -444,30 +500,30 @@ class PASELIHandler(Base):
|
|||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
session = request.child_value('sessid')
|
session = request.child_value("sessid")
|
||||||
if session is not None:
|
if session is not None:
|
||||||
# Destroy the session so it can't be used for any other purchases
|
# Destroy the session so it can't be used for any other purchases
|
||||||
self.data.local.user.destroy_session(session)
|
self.data.local.user.destroy_session(session)
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_eacoin_opcheckout_request(self, request: Node) -> Node:
|
def handle_eacoin_opcheckout_request(self, request: Node) -> Node:
|
||||||
if not self.config.paseli.enabled:
|
if not self.config.paseli.enabled:
|
||||||
# Refuse to respond, we don't have PASELI enabled
|
# Refuse to respond, we don't have PASELI enabled
|
||||||
print("PASELI not enabled, ignoring eacoin request")
|
print("PASELI not enabled, ignoring eacoin request")
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
root.set_attribute('status', str(Status.NOT_ALLOWED))
|
root.set_attribute("status", str(Status.NOT_ALLOWED))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
session = request.child_value('sessid')
|
session = request.child_value("sessid")
|
||||||
if session is not None:
|
if session is not None:
|
||||||
# Destroy the session so it can't be used for any other purchases
|
# Destroy the session so it can't be used for any other purchases
|
||||||
self.data.local.machine.destroy_session(session)
|
self.data.local.machine.destroy_session(session)
|
||||||
|
|
||||||
root = Node.void('eacoin')
|
root = Node.void("eacoin")
|
||||||
return root
|
return root
|
||||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
|||||||
|
|
||||||
from bemani.backend.base import Base
|
from bemani.backend.base import Base
|
||||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
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.data import Config, Data, Score, UserID, ScoreSaveException
|
||||||
from bemani.protocol import Node
|
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.
|
# Return the local2 service so that DDR Ace will send certain packets.
|
||||||
extra_services: List[str] = [
|
extra_services: List[str] = [
|
||||||
'local2',
|
"local2",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||||
super().__init__(data, config, model)
|
super().__init__(data, config, model)
|
||||||
if model.rev == 'X':
|
if model.rev == "X":
|
||||||
self.omnimix = True
|
self.omnimix = True
|
||||||
else:
|
else:
|
||||||
self.omnimix = False
|
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.
|
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:
|
def db_to_game_rank(self, db_rank: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a rank as defined above, return the game's rank constant.
|
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:
|
def game_to_db_chart(self, game_chart: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a game's chart for a song, return the chart as defined above.
|
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:
|
def db_to_game_chart(self, db_chart: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a chart as defined above, return the game's chart constant.
|
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:
|
def game_to_db_halo(self, game_halo: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a game's halo constant, return the halo as defined above.
|
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:
|
def db_to_game_halo(self, db_halo: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a halo as defined above, return the game's halo constant.
|
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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -118,16 +125,20 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
Base handler for a profile. Given a userid and a profile dictionary,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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 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,
|
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.
|
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
|
# Now, return it
|
||||||
return self.format_profile(userid, profile)
|
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.
|
Given a RefID and a name/area, create a new profile.
|
||||||
"""
|
"""
|
||||||
@ -169,8 +182,8 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
'area': area,
|
"area": area,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.put_profile(userid, defaultprofile)
|
self.put_profile(userid, defaultprofile)
|
||||||
@ -219,7 +232,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
self.CHART_DOUBLE_EXPERT,
|
self.CHART_DOUBLE_EXPERT,
|
||||||
self.CHART_DOUBLE_CHALLENGE,
|
self.CHART_DOUBLE_CHALLENGE,
|
||||||
]:
|
]:
|
||||||
raise Exception(f'Invalid chart {chart}')
|
raise Exception(f"Invalid chart {chart}")
|
||||||
if halo not in [
|
if halo not in [
|
||||||
self.HALO_NONE,
|
self.HALO_NONE,
|
||||||
self.HALO_GOOD_FULL_COMBO,
|
self.HALO_GOOD_FULL_COMBO,
|
||||||
@ -227,7 +240,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
self.HALO_PERFECT_FULL_COMBO,
|
self.HALO_PERFECT_FULL_COMBO,
|
||||||
self.HALO_MARVELOUS_FULL_COMBO,
|
self.HALO_MARVELOUS_FULL_COMBO,
|
||||||
]:
|
]:
|
||||||
raise Exception(f'Invalid halo {halo}')
|
raise Exception(f"Invalid halo {halo}")
|
||||||
if rank not in [
|
if rank not in [
|
||||||
self.RANK_E,
|
self.RANK_E,
|
||||||
self.RANK_D,
|
self.RANK_D,
|
||||||
@ -246,7 +259,7 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
self.RANK_AA_PLUS,
|
self.RANK_AA_PLUS,
|
||||||
self.RANK_AAA,
|
self.RANK_AAA,
|
||||||
]:
|
]:
|
||||||
raise Exception(f'Invalid rank {rank}')
|
raise Exception(f"Invalid rank {rank}")
|
||||||
|
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
oldscore = self.data.local.music.get_score(
|
oldscore = self.data.local.music.get_score(
|
||||||
@ -277,26 +290,26 @@ class DDRBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Save combo
|
# Save combo
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
|
|
||||||
# Save halo
|
# Save halo
|
||||||
history.replace_int('halo', halo)
|
history.replace_int("halo", halo)
|
||||||
scoredata.replace_int('halo', max(scoredata.get_int('halo'), halo))
|
scoredata.replace_int("halo", max(scoredata.get_int("halo"), halo))
|
||||||
|
|
||||||
# Save rank
|
# Save rank
|
||||||
history.replace_int('rank', rank)
|
history.replace_int("rank", rank)
|
||||||
scoredata.replace_int('rank', max(scoredata.get_int('rank'), rank))
|
scoredata.replace_int("rank", max(scoredata.get_int("rank"), rank))
|
||||||
|
|
||||||
# Save ghost steps
|
# Save ghost steps
|
||||||
if trace is not None:
|
if trace is not None:
|
||||||
history.replace_int_array('trace', len(trace), trace)
|
history.replace_int_array("trace", len(trace), trace)
|
||||||
if raised:
|
if raised:
|
||||||
scoredata.replace_int_array('trace', len(trace), trace)
|
scoredata.replace_int_array("trace", len(trace), trace)
|
||||||
if ghost is not None:
|
if ghost is not None:
|
||||||
history.replace_str('ghost', ghost)
|
history.replace_str("ghost", ghost)
|
||||||
if raised:
|
if raised:
|
||||||
scoredata.replace_str('ghost', ghost)
|
scoredata.replace_str("ghost", ghost)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
|
@ -7,60 +7,52 @@ from bemani.protocol import Node
|
|||||||
|
|
||||||
|
|
||||||
class DDRGameShopHandler(DDRBase):
|
class DDRGameShopHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_shop_request(self, request: Node) -> Node:
|
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 = Node.void("game")
|
||||||
game.set_attribute('stop', '0')
|
game.set_attribute("stop", "0")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameLogHandler(DDRBase):
|
class DDRGameLogHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_log_request(self, request: Node) -> Node:
|
def handle_game_log_request(self, request: Node) -> Node:
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
|
|
||||||
class DDRGameMessageHandler(DDRBase):
|
class DDRGameMessageHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_message_request(self, request: Node) -> Node:
|
def handle_game_message_request(self, request: Node) -> Node:
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
|
|
||||||
class DDRGameRankingHandler(DDRBase):
|
class DDRGameRankingHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_ranking_request(self, request: Node) -> Node:
|
def handle_game_ranking_request(self, request: Node) -> Node:
|
||||||
# Ranking request, unknown what its for
|
# Ranking request, unknown what its for
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
|
|
||||||
class DDRGameLockHandler(DDRBase):
|
class DDRGameLockHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_lock_request(self, request: Node) -> Node:
|
def handle_game_lock_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.set_attribute('now_login', '0')
|
game.set_attribute("now_login", "0")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameTaxInfoHandler(DDRBase):
|
class DDRGameTaxInfoHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_tax_info_request(self, request: Node) -> Node:
|
def handle_game_tax_info_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
tax_info = Node.void('tax_info')
|
tax_info = Node.void("tax_info")
|
||||||
game.add_child(tax_info)
|
game.add_child(tax_info)
|
||||||
tax_info.set_attribute('tax_phase', '0')
|
tax_info.set_attribute("tax_phase", "0")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameRecorderHandler(DDRBase):
|
class DDRGameRecorderHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_recorder_request(self, request: Node) -> Node:
|
def handle_game_recorder_request(self, request: Node) -> Node:
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
|
|
||||||
class DDRGameHiscoreHandler(DDRBase):
|
class DDRGameHiscoreHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_hiscore_request(self, request: Node) -> Node:
|
def handle_game_hiscore_request(self, request: Node) -> Node:
|
||||||
records = self.data.remote.music.get_all_records(self.game, self.music_version)
|
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] = {}
|
||||||
sortedrecords[score.id][score.chart] = (userid, score)
|
sortedrecords[score.id][score.chart] = (userid, score)
|
||||||
missing_profiles.append(userid)
|
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:
|
for song in sortedrecords:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink_num', str(song))
|
music.set_attribute("reclink_num", str(song))
|
||||||
|
|
||||||
for chart in sortedrecords[song]:
|
for chart in sortedrecords[song]:
|
||||||
userid, score = sortedrecords[song][chart]
|
userid, score = sortedrecords[song][chart]
|
||||||
@ -86,36 +81,44 @@ class DDRGameHiscoreHandler(DDRBase):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.string("name", users[userid].get_str("name")))
|
||||||
typenode.add_child(Node.u32('score', score.points))
|
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(
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
Node.u16(
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
"area", users[userid].get_int("area", self.get_machine_region())
|
||||||
typenode.add_child(Node.u32('code', users[userid].extid))
|
)
|
||||||
|
)
|
||||||
|
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
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameAreaHiscoreHandler(DDRBase):
|
class DDRGameAreaHiscoreHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_area_hiscore_request(self, request: Node) -> Node:
|
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
|
# First, get all users that are in the current shop's area
|
||||||
area_users = {
|
area_users = {
|
||||||
uid: prof for (uid, prof) in self.data.local.user.get_all_profiles(self.game, self.version)
|
uid: prof
|
||||||
if prof.get_int('area', self.get_machine_region()) == shop_area
|
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
|
# 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
|
# 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
|
# 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] = {}
|
||||||
area_records[score.id][score.chart] = (userid, score)
|
area_records[score.id][score.chart] = (userid, score)
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for song in area_records:
|
for song in area_records:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(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]:
|
for chart in area_records[song]:
|
||||||
userid, score = area_records[song][chart]
|
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
|
# Don't return this, this user isn't in this area
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@ -142,29 +148,35 @@ class DDRGameAreaHiscoreHandler(DDRBase):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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(
|
||||||
typenode.add_child(Node.u32('score', score.points))
|
Node.string("name", area_users[userid].get_str("name"))
|
||||||
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.u32("score", score.points))
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
typenode.add_child(
|
||||||
typenode.add_child(Node.u32('code', area_users[userid].extid))
|
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
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameScoreHandler(DDRBase):
|
class DDRGameScoreHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_score_request(self, request: Node) -> Node:
|
def handle_game_score_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
songid = int(request.attribute('mid'))
|
songid = int(request.attribute("mid"))
|
||||||
chart = self.game_to_db_chart(int(request.attribute('type')))
|
chart = self.game_to_db_chart(int(request.attribute("type")))
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
@ -185,22 +197,21 @@ class DDRGameScoreHandler(DDRBase):
|
|||||||
recentscores.append(0)
|
recentscores.append(0)
|
||||||
|
|
||||||
# Return the most recent five scores
|
# Return the most recent five scores
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for i in range(len(recentscores)):
|
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
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameTraceHandler(DDRBase):
|
class DDRGameTraceHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_trace_request(self, request: Node) -> Node:
|
def handle_game_trace_request(self, request: Node) -> Node:
|
||||||
extid = int(request.attribute('code'))
|
extid = int(request.attribute("code"))
|
||||||
chart = int(request.attribute('type'))
|
chart = int(request.attribute("type"))
|
||||||
cid = intish(request.attribute('cid'))
|
cid = intish(request.attribute("cid"))
|
||||||
mid = intish(request.attribute('mid'))
|
mid = intish(request.attribute("mid"))
|
||||||
|
|
||||||
# Base packet is just game, if we find something we add to it
|
# Base packet is just game, if we find something we add to it
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
|
|
||||||
# Rival trace loading
|
# Rival trace loading
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
@ -217,9 +228,9 @@ class DDRGameTraceHandler(DDRBase):
|
|||||||
mid,
|
mid,
|
||||||
self.game_to_db_chart(chart),
|
self.game_to_db_chart(chart),
|
||||||
)
|
)
|
||||||
if songscore is not None and 'trace' in songscore.data:
|
if songscore is not None and "trace" in songscore.data:
|
||||||
game.add_child(Node.u32('size', len(songscore.data['trace'])))
|
game.add_child(Node.u32("size", len(songscore.data["trace"])))
|
||||||
game.add_child(Node.u8_array('trace', songscore.data['trace']))
|
game.add_child(Node.u8_array("trace", songscore.data["trace"]))
|
||||||
|
|
||||||
elif cid is not None:
|
elif cid is not None:
|
||||||
# Load trace from achievement
|
# Load trace from achievement
|
||||||
@ -228,35 +239,33 @@ class DDRGameTraceHandler(DDRBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
(cid * 4) + chart,
|
(cid * 4) + chart,
|
||||||
'course',
|
"course",
|
||||||
)
|
)
|
||||||
if coursescore is not None and 'trace' in coursescore:
|
if coursescore is not None and "trace" in coursescore:
|
||||||
game.add_child(Node.u32('size', len(coursescore['trace'])))
|
game.add_child(Node.u32("size", len(coursescore["trace"])))
|
||||||
game.add_child(Node.u8_array('trace', coursescore['trace']))
|
game.add_child(Node.u8_array("trace", coursescore["trace"]))
|
||||||
|
|
||||||
# Nothing found, return empty
|
# Nothing found, return empty
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameLoadHandler(DDRBase):
|
class DDRGameLoadHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_load_request(self, request: Node) -> Node:
|
def handle_game_load_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
profile = self.get_profile_by_refid(refid)
|
profile = self.get_profile_by_refid(refid)
|
||||||
if profile is not None:
|
if profile is not None:
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.set_attribute('none', '0')
|
game.set_attribute("none", "0")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameLoadDailyHandler(DDRBase):
|
class DDRGameLoadDailyHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_load_daily_request(self, request: Node) -> Node:
|
def handle_game_load_daily_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
profiledict = None
|
profiledict = None
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
@ -272,24 +281,23 @@ class DDRGameLoadDailyHandler(DDRBase):
|
|||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Day play counts
|
# Day play counts
|
||||||
daycount = Node.void('daycount')
|
daycount = Node.void("daycount")
|
||||||
game.add_child(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
|
# Daily combo stuff, unclear how this works
|
||||||
dailycombo = Node.void('dailycombo')
|
dailycombo = Node.void("dailycombo")
|
||||||
game.add_child(dailycombo)
|
game.add_child(dailycombo)
|
||||||
dailycombo.set_attribute('daily_combo', str(0))
|
dailycombo.set_attribute("daily_combo", str(0))
|
||||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameOldHandler(DDRBase):
|
class DDRGameOldHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_old_request(self, request: Node) -> Node:
|
def handle_game_old_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
previous_version: Optional[DDRBase] = None
|
previous_version: Optional[DDRBase] = None
|
||||||
@ -300,41 +308,40 @@ class DDRGameOldHandler(DDRBase):
|
|||||||
if previous_version is not None:
|
if previous_version is not None:
|
||||||
oldprofile = previous_version.get_profile(userid)
|
oldprofile = previous_version.get_profile(userid)
|
||||||
if oldprofile is not None:
|
if oldprofile is not None:
|
||||||
game.set_attribute('name', oldprofile.get_str('name'))
|
game.set_attribute("name", oldprofile.get_str("name"))
|
||||||
game.set_attribute('area', str(oldprofile.get_int('area', self.get_machine_region())))
|
game.set_attribute(
|
||||||
|
"area", str(oldprofile.get_int("area", self.get_machine_region()))
|
||||||
|
)
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameNewHandler(DDRBase):
|
class DDRGameNewHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_new_request(self, request: Node) -> Node:
|
def handle_game_new_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
area = int(request.attribute('area'))
|
area = int(request.attribute("area"))
|
||||||
name = request.attribute('name').strip()
|
name = request.attribute("name").strip()
|
||||||
|
|
||||||
# Create a new profile for this user!
|
# Create a new profile for this user!
|
||||||
self.new_profile_by_refid(refid, name, area)
|
self.new_profile_by_refid(refid, name, area)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameSaveHandler(DDRBase):
|
class DDRGameSaveHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_save_request(self, request: Node) -> Node:
|
def handle_game_save_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
self.put_profile_by_refid(refid, request)
|
self.put_profile_by_refid(refid, request)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameFriendHandler(DDRBase):
|
class DDRGameFriendHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_friend_request(self, request: Node) -> Node:
|
def handle_game_friend_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
userid = None
|
userid = None
|
||||||
friend = None
|
friend = None
|
||||||
|
|
||||||
@ -347,62 +354,63 @@ class DDRGameFriendHandler(DDRBase):
|
|||||||
|
|
||||||
if friend is None:
|
if friend is None:
|
||||||
# Return an empty node to tell the game we don't have a player here
|
# 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
|
return game
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.set_attribute('data', '1')
|
game.set_attribute("data", "1")
|
||||||
game.add_child(Node.u32('code', friend.extid))
|
game.add_child(Node.u32("code", friend.extid))
|
||||||
game.add_child(Node.string('name', friend.get_str('name')))
|
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(
|
||||||
game.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||||
game.add_child(Node.u32('star', friend.get_int('star')))
|
)
|
||||||
|
game.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
|
game.add_child(Node.u32("star", friend.get_int("star")))
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title' in friend:
|
if "title" in friend:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
game.add_child(title)
|
game.add_child(title)
|
||||||
titledict = friend.get_dict('title')
|
titledict = friend.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in friend:
|
if "title_gr" in friend:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
game.add_child(title_gr)
|
game.add_child(title_gr)
|
||||||
title_grdict = friend.get_dict('title_gr')
|
title_grdict = friend.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
game.add_child(gr_s)
|
game.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_s', 5):
|
for entry in friend.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
game.add_child(gr_d)
|
game.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_d', 5):
|
for entry in friend.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameLoadCourseHandler(DDRBase):
|
class DDRGameLoadCourseHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_load_c_request(self, request: Node) -> Node:
|
def handle_game_load_c_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
# Rival score loading
|
# Rival score loading
|
||||||
@ -413,8 +421,10 @@ class DDRGameLoadCourseHandler(DDRBase):
|
|||||||
|
|
||||||
coursedata = [0] * 3200
|
coursedata = [0] * 3200
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
for course in self.data.local.user.get_achievements(self.game, self.version, userid):
|
for course in self.data.local.user.get_achievements(
|
||||||
if course.type != 'course':
|
self.game, self.version, userid
|
||||||
|
):
|
||||||
|
if course.type != "course":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Grab course ID and chart (kinda pointless because we add it right back up
|
# Grab course ID and chart (kinda pointless because we add it right back up
|
||||||
@ -425,35 +435,36 @@ class DDRGameLoadCourseHandler(DDRBase):
|
|||||||
# Populate course data
|
# Populate course data
|
||||||
index = ((courseid * 4) + coursechart) * 8
|
index = ((courseid * 4) + coursechart) * 8
|
||||||
if index >= 0 and index <= (len(coursedata) - 8):
|
if index >= 0 and index <= (len(coursedata) - 8):
|
||||||
coursedata[index + 0] = int(course.data.get_int('score') / 10000)
|
coursedata[index + 0] = int(course.data.get_int("score") / 10000)
|
||||||
coursedata[index + 1] = 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 + 2] = course.data.get_int("combo")
|
||||||
coursedata[index + 3] = self.db_to_game_rank(course.data.get_int('rank'))
|
coursedata[index + 3] = self.db_to_game_rank(
|
||||||
coursedata[index + 5] = course.data.get_int('stage')
|
course.data.get_int("rank")
|
||||||
coursedata[index + 6] = course.data.get_int('combo_type')
|
)
|
||||||
|
coursedata[index + 5] = course.data.get_int("stage")
|
||||||
|
coursedata[index + 6] = course.data.get_int("combo_type")
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.add_child(Node.u16_array('course', coursedata))
|
game.add_child(Node.u16_array("course", coursedata))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class DDRGameSaveCourseHandler(DDRBase):
|
class DDRGameSaveCourseHandler(DDRBase):
|
||||||
|
|
||||||
def handle_game_save_c_request(self, request: Node) -> Node:
|
def handle_game_save_c_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
courseid = int(request.attribute('cid'))
|
courseid = int(request.attribute("cid"))
|
||||||
chart = int(request.attribute('ctype'))
|
chart = int(request.attribute("ctype"))
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
points = int(data.attribute('score'))
|
points = int(data.attribute("score"))
|
||||||
combo = int(data.attribute('combo'))
|
combo = int(data.attribute("combo"))
|
||||||
combo_type = int(data.attribute('combo_type'))
|
combo_type = int(data.attribute("combo_type"))
|
||||||
stage = int(data.attribute('stage'))
|
stage = int(data.attribute("stage"))
|
||||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||||
trace = request.child_value('trace')
|
trace = request.child_value("trace")
|
||||||
|
|
||||||
# Grab the old course score
|
# Grab the old course score
|
||||||
oldcourse = self.data.local.user.get_achievement(
|
oldcourse = self.data.local.user.get_achievement(
|
||||||
@ -461,38 +472,38 @@ class DDRGameSaveCourseHandler(DDRBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
(courseid * 4) + chart,
|
(courseid * 4) + chart,
|
||||||
'course',
|
"course",
|
||||||
)
|
)
|
||||||
|
|
||||||
if oldcourse is not None:
|
if oldcourse is not None:
|
||||||
highscore = points > oldcourse.get_int('score')
|
highscore = points > oldcourse.get_int("score")
|
||||||
|
|
||||||
points = max(points, oldcourse.get_int('score'))
|
points = max(points, oldcourse.get_int("score"))
|
||||||
combo = max(combo, oldcourse.get_int('combo'))
|
combo = max(combo, oldcourse.get_int("combo"))
|
||||||
stage = max(stage, oldcourse.get_int('stage'))
|
stage = max(stage, oldcourse.get_int("stage"))
|
||||||
rank = max(rank, oldcourse.get_int('rank'))
|
rank = max(rank, oldcourse.get_int("rank"))
|
||||||
combo_type = max(combo_type, oldcourse.get_int('combo_type'))
|
combo_type = max(combo_type, oldcourse.get_int("combo_type"))
|
||||||
|
|
||||||
if not highscore:
|
if not highscore:
|
||||||
# Don't overwrite the ghost for a non-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.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
(courseid * 4) + chart,
|
(courseid * 4) + chart,
|
||||||
'course',
|
"course",
|
||||||
{
|
{
|
||||||
'score': points,
|
"score": points,
|
||||||
'combo': combo,
|
"combo": combo,
|
||||||
'stage': stage,
|
"stage": stage,
|
||||||
'rank': rank,
|
"rank": rank,
|
||||||
'combo_type': combo_type,
|
"combo_type": combo_type,
|
||||||
'trace': trace,
|
"trace": trace,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
@ -53,7 +53,7 @@ class DDR2013(
|
|||||||
DDRBase,
|
DDRBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 2013'
|
name: str = "DanceDanceRevolution 2013"
|
||||||
version: int = VersionConstants.DDR_2013
|
version: int = VersionConstants.DDR_2013
|
||||||
|
|
||||||
GAME_STYLE_SINGLE: Final[int] = 0
|
GAME_STYLE_SINGLE: Final[int] = 0
|
||||||
@ -160,30 +160,32 @@ class DDR2013(
|
|||||||
return combo_type
|
return combo_type
|
||||||
|
|
||||||
def handle_game_common_request(self, request: Node) -> Node:
|
def handle_game_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for flagid in range(256):
|
for flagid in range(256):
|
||||||
flag = Node.void('flag')
|
flag = Node.void("flag")
|
||||||
game.add_child(flag)
|
game.add_child(flag)
|
||||||
|
|
||||||
flag.set_attribute('id', str(flagid))
|
flag.set_attribute("id", str(flagid))
|
||||||
flag.set_attribute('t', '0')
|
flag.set_attribute("t", "0")
|
||||||
flag.set_attribute('s1', '0')
|
flag.set_attribute("s1", "0")
|
||||||
flag.set_attribute('s2', '0')
|
flag.set_attribute("s2", "0")
|
||||||
flag.set_attribute('area', str(self.get_machine_region()))
|
flag.set_attribute("area", str(self.get_machine_region()))
|
||||||
flag.set_attribute('is_final', '1')
|
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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
return game
|
||||||
|
|
||||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
# Rival score loading
|
# Rival score loading
|
||||||
@ -193,7 +195,9 @@ class DDR2013(
|
|||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
|
|
||||||
if userid is not 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:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
@ -203,11 +207,11 @@ class DDR2013(
|
|||||||
sortedscores[score.id] = {}
|
sortedscores[score.id] = {}
|
||||||
sortedscores[score.id][score.chart] = score
|
sortedscores[score.id][score.chart] = score
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for song in sortedscores:
|
for song in sortedscores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink', str(song))
|
music.set_attribute("reclink", str(song))
|
||||||
|
|
||||||
for chart in sortedscores[song]:
|
for chart in sortedscores[song]:
|
||||||
score = sortedscores[song][chart]
|
score = sortedscores[song][chart]
|
||||||
@ -216,41 +220,41 @@ class DDR2013(
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.u32("score", score.points))
|
||||||
typenode.add_child(Node.u16('count', score.plays))
|
typenode.add_child(Node.u16("count", score.plays))
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
typenode.add_child(Node.u8("rank", gamerank))
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
songid = int(request.attribute('mid'))
|
songid = int(request.attribute("mid"))
|
||||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
points = int(data.attribute('score'))
|
points = int(data.attribute("score"))
|
||||||
combo = int(data.attribute('combo'))
|
combo = int(data.attribute("combo"))
|
||||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||||
if points == 1000000:
|
if points == 1000000:
|
||||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
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
|
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
|
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
|
halo = self.HALO_GOOD_FULL_COMBO
|
||||||
else:
|
else:
|
||||||
halo = self.HALO_NONE
|
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
|
# 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
|
# an anonymous score, it only goes into the DB to count against the
|
||||||
@ -268,355 +272,371 @@ class DDR2013(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
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
|
# Look up play stats we bridge to every mix
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Basic game settings
|
# Basic game settings
|
||||||
root.add_child(Node.string('seq', ''))
|
root.add_child(Node.string("seq", ""))
|
||||||
root.add_child(Node.u32('code', profile.extid))
|
root.add_child(Node.u32("code", profile.extid))
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
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(
|
||||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||||
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_s", play_stats.get_int("single_plays")))
|
||||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
root.add_child(
|
||||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
) # This could be wrong, its a guess
|
||||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
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
|
# Character stuff
|
||||||
chara = Node.void('chara')
|
chara = Node.void("chara")
|
||||||
root.add_child(chara)
|
root.add_child(chara)
|
||||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
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)))
|
root.add_child(
|
||||||
|
Node.u16_array(
|
||||||
|
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title_gr' in profile:
|
if "title_gr" in profile:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
root.add_child(title_gr)
|
root.add_child(title_gr)
|
||||||
title_grdict = profile.get_dict('title_gr')
|
title_grdict = profile.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Calorie mode
|
# Calorie mode
|
||||||
if 'weight' in profile:
|
if "weight" in profile:
|
||||||
workouts = self.data.local.user.get_time_based_achievements(
|
workouts = self.data.local.user.get_time_based_achievements(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
achievementtype='workout',
|
achievementtype="workout",
|
||||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
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])
|
||||||
workout = Node.void('workout')
|
workout = Node.void("workout")
|
||||||
root.add_child(workout)
|
root.add_child(workout)
|
||||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||||
workout.set_attribute('day', str(total))
|
workout.set_attribute("day", str(total))
|
||||||
workout.set_attribute('disp', '1')
|
workout.set_attribute("disp", "1")
|
||||||
|
|
||||||
# Daily play counts
|
# Daily play counts
|
||||||
daycount = Node.void('daycount')
|
daycount = Node.void("daycount")
|
||||||
root.add_child(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
|
# Daily combo stuff, unknown how this works
|
||||||
dailycombo = Node.void('dailycombo')
|
dailycombo = Node.void("dailycombo")
|
||||||
root.add_child(dailycombo)
|
root.add_child(dailycombo)
|
||||||
dailycombo.set_attribute('daily_combo', str(0))
|
dailycombo.set_attribute("daily_combo", str(0))
|
||||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||||
|
|
||||||
# Last cursor settings
|
# Last cursor settings
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
root.add_child(last)
|
root.add_child(last)
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -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(
|
||||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
"fri", str(lastdict.get_int("rival1", -1))
|
||||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
) # This literally goes to the same memory in 2013
|
||||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
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 stars
|
||||||
result_star = Node.void('result_star')
|
result_star = Node.void("result_star")
|
||||||
root.add_child(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):
|
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 stuff
|
||||||
target = Node.void('target')
|
target = Node.void("target")
|
||||||
root.add_child(target)
|
root.add_child(target)
|
||||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
root.add_child(gr_s)
|
root.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_s', 5):
|
for entry in profile.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
root.add_child(gr_d)
|
root.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_d', 5):
|
for entry in profile.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Options in menus
|
# Options in menus
|
||||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 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)))
|
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||||
|
|
||||||
# Unlock flags
|
# 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?
|
# 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
|
# Rivals
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
friend = self.get_profile(link.other_userid)
|
friend = self.get_profile(link.other_userid)
|
||||||
play_stats = self.get_play_statistics(link.other_userid)
|
play_stats = self.get_play_statistics(link.other_userid)
|
||||||
if friend is not None:
|
if friend is not None:
|
||||||
friendnode = Node.void('friend')
|
friendnode = Node.void("friend")
|
||||||
root.add_child(friendnode)
|
root.add_child(friendnode)
|
||||||
friendnode.set_attribute('pos', str(pos))
|
friendnode.set_attribute("pos", str(pos))
|
||||||
friendnode.set_attribute('vs', '0')
|
friendnode.set_attribute("vs", "0")
|
||||||
friendnode.set_attribute('up', '0')
|
friendnode.set_attribute("up", "0")
|
||||||
friendnode.add_child(Node.u32('code', friend.extid))
|
friendnode.add_child(Node.u32("code", friend.extid))
|
||||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
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(
|
||||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
)
|
||||||
|
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
|
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title' in friend:
|
if "title" in friend:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
friendnode.add_child(title)
|
friendnode.add_child(title)
|
||||||
titledict = friend.get_dict('title')
|
titledict = friend.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in friend:
|
if "title_gr" in friend:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
friendnode.add_child(title_gr)
|
friendnode.add_child(title_gr)
|
||||||
title_grdict = friend.get_dict('title_gr')
|
title_grdict = friend.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
friendnode.add_child(gr_s)
|
friendnode.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_s', 5):
|
for entry in friend.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
friendnode.add_child(gr_d)
|
friendnode.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_d', 5):
|
for entry in friend.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Play area
|
# Play area
|
||||||
areas = profile.get_int_array('play_area', 55)
|
areas = profile.get_int_array("play_area", 55)
|
||||||
play_area = Node.void('play_area')
|
play_area = Node.void("play_area")
|
||||||
root.add_child(play_area)
|
root.add_child(play_area)
|
||||||
for i in range(len(areas)):
|
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
|
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()
|
newprofile = oldprofile.clone()
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Grab last node and accessories so we can make decisions based on type
|
# Grab last node and accessories so we can make decisions based on type
|
||||||
last = request.child('last')
|
last = request.child("last")
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
mode = int(last.attribute('mode'))
|
mode = int(last.attribute("mode"))
|
||||||
style = int(last.attribute('style'))
|
style = int(last.attribute("style"))
|
||||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
title = request.child('title')
|
title = request.child("title")
|
||||||
title_gr = request.child('title_gr')
|
title_gr = request.child("title_gr")
|
||||||
titledict = newprofile.get_dict('title')
|
titledict = newprofile.get_dict("title")
|
||||||
title_grdict = newprofile.get_dict('title_gr')
|
title_grdict = newprofile.get_dict("title_gr")
|
||||||
|
|
||||||
# Groove radar level ups
|
# 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
|
# Set the correct values depending on if we're single or double play
|
||||||
if is_dp:
|
if is_dp:
|
||||||
play_stats.increment_int('double_plays')
|
play_stats.increment_int("double_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_d',
|
"gr_d",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('d', title.value)
|
titledict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('d', title.value)
|
title_grdict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
else:
|
else:
|
||||||
play_stats.increment_int('single_plays')
|
play_stats.increment_int("single_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_s',
|
"gr_s",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('s', title.value)
|
titledict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('s', title.value)
|
title_grdict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
play_stats.increment_int(f'cnt_m{mode}')
|
play_stats.increment_int(f"cnt_m{mode}")
|
||||||
|
|
||||||
# Result stars
|
# Result stars
|
||||||
result_star = request.child('result_star')
|
result_star = request.child("result_star")
|
||||||
if result_star is not None:
|
if result_star is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'result_stars',
|
"result_stars",
|
||||||
9,
|
9,
|
||||||
[
|
[
|
||||||
intish(result_star.attribute('slot1')),
|
intish(result_star.attribute("slot1")),
|
||||||
intish(result_star.attribute('slot2')),
|
intish(result_star.attribute("slot2")),
|
||||||
intish(result_star.attribute('slot3')),
|
intish(result_star.attribute("slot3")),
|
||||||
intish(result_star.attribute('slot4')),
|
intish(result_star.attribute("slot4")),
|
||||||
intish(result_star.attribute('slot5')),
|
intish(result_star.attribute("slot5")),
|
||||||
intish(result_star.attribute('slot6')),
|
intish(result_star.attribute("slot6")),
|
||||||
intish(result_star.attribute('slot7')),
|
intish(result_star.attribute("slot7")),
|
||||||
intish(result_star.attribute('slot8')),
|
intish(result_star.attribute("slot8")),
|
||||||
intish(result_star.attribute('slot9')),
|
intish(result_star.attribute("slot9")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Target stuff
|
# Target stuff
|
||||||
target = request.child('target')
|
target = request.child("target")
|
||||||
if target is not None:
|
if target is not None:
|
||||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||||
|
|
||||||
# Update last attributes
|
# Update last attributes
|
||||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Grab character options
|
# Grab character options
|
||||||
chara = request.child('chara')
|
chara = request.child("chara")
|
||||||
if chara is not None:
|
if chara is not None:
|
||||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||||
chara_opt = request.child('chara_opt')
|
chara_opt = request.child("chara_opt")
|
||||||
if chara_opt is not None:
|
if chara_opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Options
|
||||||
opt = request.child('opt')
|
opt = request.child("opt")
|
||||||
if opt is not None:
|
if opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Experience and stars
|
||||||
exp = request.child_value('exp')
|
exp = request.child_value("exp")
|
||||||
if exp is not None:
|
if exp is not None:
|
||||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||||
star = request.child_value('star')
|
star = request.child_value("star")
|
||||||
if star is not None:
|
if star is not None:
|
||||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||||
star_c = request.child_value('star_c')
|
star_c = request.child_value("star_c")
|
||||||
if star_c is not None:
|
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
|
# Update game flags
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'flag':
|
if child.name != "flag":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
value = int(child.attribute('data'))
|
value = int(child.attribute("data"))
|
||||||
offset = int(child.attribute('no'))
|
offset = int(child.attribute("no"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
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):
|
if offset < 0 or offset >= len(flags):
|
||||||
continue
|
continue
|
||||||
flags[offset] = value
|
flags[offset] = value
|
||||||
newprofile.replace_int_array('flag', 256, flags)
|
newprofile.replace_int_array("flag", 256, flags)
|
||||||
|
|
||||||
# Workout mode support
|
# Workout mode support
|
||||||
newweight = -1
|
newweight = -1
|
||||||
oldweight = newprofile.get_int('weight')
|
oldweight = newprofile.get_int("weight")
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'weight':
|
if child.name != "weight":
|
||||||
continue
|
continue
|
||||||
newweight = child.value
|
newweight = child.value
|
||||||
if newweight < 0:
|
if newweight < 0:
|
||||||
@ -625,14 +645,14 @@ class DDR2013(
|
|||||||
# Either update or unset the weight depending on the game
|
# Either update or unset the weight depending on the game
|
||||||
if newweight == 0:
|
if newweight == 0:
|
||||||
# Weight is unset or we declined to use this feature, remove from profile
|
# Weight is unset or we declined to use this feature, remove from profile
|
||||||
if 'weight' in newprofile:
|
if "weight" in newprofile:
|
||||||
del newprofile['weight']
|
del newprofile["weight"]
|
||||||
else:
|
else:
|
||||||
# Weight has been set or previously retrieved, we should save calories
|
# Weight has been set or previously retrieved, we should save calories
|
||||||
newprofile.replace_int('weight', newweight)
|
newprofile.replace_int("weight", newweight)
|
||||||
total = 0
|
total = 0
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'calory':
|
if child.name != "calory":
|
||||||
continue
|
continue
|
||||||
total += child.value
|
total += child.value
|
||||||
self.data.local.user.put_time_based_achievement(
|
self.data.local.user.put_time_based_achievement(
|
||||||
@ -640,10 +660,10 @@ class DDR2013(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
0,
|
0,
|
||||||
'workout',
|
"workout",
|
||||||
{
|
{
|
||||||
'calories': total,
|
"calories": total,
|
||||||
'weight': newweight,
|
"weight": newweight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -651,7 +671,7 @@ class DDR2013(
|
|||||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
@ -660,11 +680,11 @@ class DDR2013(
|
|||||||
# Save any rivals that were added/removed/changed
|
# Save any rivals that were added/removed/changed
|
||||||
newfriends = oldfriends[:]
|
newfriends = oldfriends[:]
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'friend':
|
if child.name != "friend":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
code = int(child.attribute('code'))
|
code = int(child.attribute("code"))
|
||||||
pos = int(child.attribute('pos'))
|
pos = int(child.attribute("pos"))
|
||||||
|
|
||||||
if pos >= 0 and pos < 10:
|
if pos >= 0 and pos < 10:
|
||||||
if code == 0:
|
if code == 0:
|
||||||
@ -672,7 +692,9 @@ class DDR2013(
|
|||||||
newfriends[pos] = None
|
newfriends[pos] = None
|
||||||
else:
|
else:
|
||||||
# Try looking up the userid
|
# 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
|
# Diff the set of links to determine updates
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
@ -685,7 +707,7 @@ class DDR2013(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
elif oldfriends[i] is None:
|
elif oldfriends[i] is None:
|
||||||
@ -694,7 +716,7 @@ class DDR2013(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
@ -704,24 +726,24 @@ class DDR2013(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
self.data.local.user.put_link(
|
self.data.local.user.put_link(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Play area counter
|
# 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:
|
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
|
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
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid, play_stats)
|
self.update_play_statistics(userid, play_stats)
|
||||||
|
@ -47,7 +47,7 @@ class DDR2014(
|
|||||||
DDRBase,
|
DDRBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 2014'
|
name: str = "DanceDanceRevolution 2014"
|
||||||
version: int = VersionConstants.DDR_2014
|
version: int = VersionConstants.DDR_2014
|
||||||
|
|
||||||
GAME_STYLE_SINGLE: Final[int] = 0
|
GAME_STYLE_SINGLE: Final[int] = 0
|
||||||
@ -154,47 +154,53 @@ class DDR2014(
|
|||||||
return combo_type
|
return combo_type
|
||||||
|
|
||||||
def handle_game_common_request(self, request: Node) -> Node:
|
def handle_game_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for flagid in range(512):
|
for flagid in range(512):
|
||||||
flag = Node.void('flag')
|
flag = Node.void("flag")
|
||||||
game.add_child(flag)
|
game.add_child(flag)
|
||||||
|
|
||||||
flag.set_attribute('id', str(flagid))
|
flag.set_attribute("id", str(flagid))
|
||||||
flag.set_attribute('t', '0')
|
flag.set_attribute("t", "0")
|
||||||
flag.set_attribute('s1', '0')
|
flag.set_attribute("s1", "0")
|
||||||
flag.set_attribute('s2', '0')
|
flag.set_attribute("s2", "0")
|
||||||
flag.set_attribute('area', str(self.get_machine_region()))
|
flag.set_attribute("area", str(self.get_machine_region()))
|
||||||
flag.set_attribute('is_final', '0')
|
flag.set_attribute("is_final", "0")
|
||||||
|
|
||||||
# Last month's hit chart
|
# 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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
# 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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
# 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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
return game
|
||||||
|
|
||||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
# Rival score loading
|
# Rival score loading
|
||||||
@ -204,7 +210,9 @@ class DDR2014(
|
|||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
|
|
||||||
if userid is not 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:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
@ -214,11 +222,11 @@ class DDR2014(
|
|||||||
sortedscores[score.id] = {}
|
sortedscores[score.id] = {}
|
||||||
sortedscores[score.id][score.chart] = score
|
sortedscores[score.id][score.chart] = score
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for song in sortedscores:
|
for song in sortedscores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink', str(song))
|
music.set_attribute("reclink", str(song))
|
||||||
|
|
||||||
for chart in sortedscores[song]:
|
for chart in sortedscores[song]:
|
||||||
score = sortedscores[song][chart]
|
score = sortedscores[song][chart]
|
||||||
@ -227,17 +235,17 @@ class DDR2014(
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.u32("score", score.points))
|
||||||
typenode.add_child(Node.u16('count', score.plays))
|
typenode.add_child(Node.u16("count", score.plays))
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
typenode.add_child(Node.u8("rank", gamerank))
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||||
# The game optionally receives hard, life8, life4, risky, assist_clear, normal_clear
|
# 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
|
# 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.
|
# do anything with them, so we don't care.
|
||||||
@ -245,26 +253,26 @@ class DDR2014(
|
|||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
songid = int(request.attribute('mid'))
|
songid = int(request.attribute("mid"))
|
||||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
points = int(data.attribute('score'))
|
points = int(data.attribute("score"))
|
||||||
combo = int(data.attribute('combo'))
|
combo = int(data.attribute("combo"))
|
||||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||||
if points == 1000000:
|
if points == 1000000:
|
||||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
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
|
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
|
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
|
halo = self.HALO_GOOD_FULL_COMBO
|
||||||
else:
|
else:
|
||||||
halo = self.HALO_NONE
|
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
|
# 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
|
# an anonymous score, it only goes into the DB to count against the
|
||||||
@ -282,24 +290,24 @@ class DDR2014(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_load_edit_request(self, request: Node) -> Node:
|
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:
|
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:
|
def handle_game_trace_request(self, request: Node) -> Node:
|
||||||
# This is almost identical to 2013 and below, except it will never
|
# This is almost identical to 2013 and below, except it will never
|
||||||
# even try to request course traces, so we fork from common functionality.
|
# even try to request course traces, so we fork from common functionality.
|
||||||
extid = int(request.attribute('code'))
|
extid = int(request.attribute("code"))
|
||||||
chart = int(request.attribute('type'))
|
chart = int(request.attribute("type"))
|
||||||
mid = intish(request.attribute('mid'))
|
mid = intish(request.attribute("mid"))
|
||||||
|
|
||||||
# Base packet is just game, if we find something we add to it
|
# Base packet is just game, if we find something we add to it
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
|
|
||||||
# Rival trace loading
|
# Rival trace loading
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
@ -315,369 +323,389 @@ class DDR2014(
|
|||||||
mid,
|
mid,
|
||||||
self.game_to_db_chart(chart),
|
self.game_to_db_chart(chart),
|
||||||
)
|
)
|
||||||
if songscore is not None and 'trace' in songscore.data:
|
if songscore is not None and "trace" in songscore.data:
|
||||||
game.add_child(Node.u32('size', len(songscore.data['trace'])))
|
game.add_child(Node.u32("size", len(songscore.data["trace"])))
|
||||||
game.add_child(Node.u8_array('trace', songscore.data['trace']))
|
game.add_child(Node.u8_array("trace", songscore.data["trace"]))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
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
|
# Look up play stats we bridge to every mix
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Basic game settings
|
# Basic game settings
|
||||||
root.add_child(Node.string('seq', ''))
|
root.add_child(Node.string("seq", ""))
|
||||||
root.add_child(Node.u32('code', profile.extid))
|
root.add_child(Node.u32("code", profile.extid))
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
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(
|
||||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||||
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_s", play_stats.get_int("single_plays")))
|
||||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
root.add_child(
|
||||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
) # This could be wrong, its a guess
|
||||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
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
|
# Character stuff
|
||||||
chara = Node.void('chara')
|
chara = Node.void("chara")
|
||||||
root.add_child(chara)
|
root.add_child(chara)
|
||||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
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)))
|
root.add_child(
|
||||||
|
Node.u16_array(
|
||||||
|
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title_gr' in profile:
|
if "title_gr" in profile:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
root.add_child(title_gr)
|
root.add_child(title_gr)
|
||||||
title_grdict = profile.get_dict('title_gr')
|
title_grdict = profile.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Calorie mode
|
# Calorie mode
|
||||||
if 'weight' in profile:
|
if "weight" in profile:
|
||||||
workouts = self.data.local.user.get_time_based_achievements(
|
workouts = self.data.local.user.get_time_based_achievements(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
achievementtype='workout',
|
achievementtype="workout",
|
||||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
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])
|
||||||
workout = Node.void('workout')
|
workout = Node.void("workout")
|
||||||
root.add_child(workout)
|
root.add_child(workout)
|
||||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||||
workout.set_attribute('day', str(total))
|
workout.set_attribute("day", str(total))
|
||||||
workout.set_attribute('disp', '1')
|
workout.set_attribute("disp", "1")
|
||||||
|
|
||||||
# Unsure if this should be last day, or total calories ever
|
# Unsure if this should be last day, or total calories ever
|
||||||
totalcalorie = Node.void('totalcalorie')
|
totalcalorie = Node.void("totalcalorie")
|
||||||
root.add_child(totalcalorie)
|
root.add_child(totalcalorie)
|
||||||
totalcalorie.set_attribute('total', str(total))
|
totalcalorie.set_attribute("total", str(total))
|
||||||
|
|
||||||
# Daily play counts
|
# Daily play counts
|
||||||
daycount = Node.void('daycount')
|
daycount = Node.void("daycount")
|
||||||
root.add_child(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
|
# Daily combo stuff, unknown how this works
|
||||||
dailycombo = Node.void('dailycombo')
|
dailycombo = Node.void("dailycombo")
|
||||||
root.add_child(dailycombo)
|
root.add_child(dailycombo)
|
||||||
dailycombo.set_attribute('daily_combo', str(0))
|
dailycombo.set_attribute("daily_combo", str(0))
|
||||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||||
|
|
||||||
# Last cursor settings
|
# Last cursor settings
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
root.add_child(last)
|
root.add_child(last)
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -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(
|
||||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
"fri", str(lastdict.get_int("rival1", -1))
|
||||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
) # This literally goes to the same memory in 2014
|
||||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
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 stars
|
||||||
result_star = Node.void('result_star')
|
result_star = Node.void("result_star")
|
||||||
root.add_child(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):
|
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
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
root.add_child(gr_s)
|
root.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_s', 5):
|
for entry in profile.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
root.add_child(gr_d)
|
root.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_d', 5):
|
for entry in profile.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Options in menus
|
# Options in menus
|
||||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 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)))
|
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||||
option_ver = Node.void('option_ver')
|
option_ver = Node.void("option_ver")
|
||||||
root.add_child(option_ver)
|
root.add_child(option_ver)
|
||||||
option_ver.set_attribute('ver', str(profile.get_int('option_ver', 2)))
|
option_ver.set_attribute("ver", str(profile.get_int("option_ver", 2)))
|
||||||
if 'option_02' in profile:
|
if "option_02" in profile:
|
||||||
root.add_child(Node.s16_array('option_02', profile.get_int_array('option_02', 24)))
|
root.add_child(
|
||||||
|
Node.s16_array("option_02", profile.get_int_array("option_02", 24))
|
||||||
|
)
|
||||||
|
|
||||||
# Unlock flags
|
# Unlock flags
|
||||||
root.add_child(Node.u8_array('flag', profile.get_int_array('flag', 512, [1] * 512)[:256]))
|
root.add_child(
|
||||||
root.add_child(Node.u8_array('flag_ex', profile.get_int_array('flag', 512, [1] * 512)))
|
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?
|
# 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
|
# Rivals
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
friend = self.get_profile(link.other_userid)
|
friend = self.get_profile(link.other_userid)
|
||||||
play_stats = self.get_play_statistics(link.other_userid)
|
play_stats = self.get_play_statistics(link.other_userid)
|
||||||
if friend is not None:
|
if friend is not None:
|
||||||
friendnode = Node.void('friend')
|
friendnode = Node.void("friend")
|
||||||
root.add_child(friendnode)
|
root.add_child(friendnode)
|
||||||
friendnode.set_attribute('pos', str(pos))
|
friendnode.set_attribute("pos", str(pos))
|
||||||
friendnode.set_attribute('vs', '0')
|
friendnode.set_attribute("vs", "0")
|
||||||
friendnode.set_attribute('up', '0')
|
friendnode.set_attribute("up", "0")
|
||||||
friendnode.add_child(Node.u32('code', friend.extid))
|
friendnode.add_child(Node.u32("code", friend.extid))
|
||||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
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(
|
||||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
)
|
||||||
|
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
|
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title' in friend:
|
if "title" in friend:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
friendnode.add_child(title)
|
friendnode.add_child(title)
|
||||||
titledict = friend.get_dict('title')
|
titledict = friend.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in friend:
|
if "title_gr" in friend:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
friendnode.add_child(title_gr)
|
friendnode.add_child(title_gr)
|
||||||
title_grdict = friend.get_dict('title_gr')
|
title_grdict = friend.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
friendnode.add_child(gr_s)
|
friendnode.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_s', 5):
|
for entry in friend.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
friendnode.add_child(gr_d)
|
friendnode.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_d', 5):
|
for entry in friend.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Target stuff
|
# Target stuff
|
||||||
target = Node.void('target')
|
target = Node.void("target")
|
||||||
root.add_child(target)
|
root.add_child(target)
|
||||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||||
|
|
||||||
# Play area
|
# Play area
|
||||||
areas = profile.get_int_array('play_area', 55)
|
areas = profile.get_int_array("play_area", 55)
|
||||||
play_area = Node.void('play_area')
|
play_area = Node.void("play_area")
|
||||||
root.add_child(play_area)
|
root.add_child(play_area)
|
||||||
for i in range(len(areas)):
|
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
|
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()
|
newprofile = oldprofile.clone()
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Grab last node and accessories so we can make decisions based on type
|
# Grab last node and accessories so we can make decisions based on type
|
||||||
last = request.child('last')
|
last = request.child("last")
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
mode = int(last.attribute('mode'))
|
mode = int(last.attribute("mode"))
|
||||||
style = int(last.attribute('style'))
|
style = int(last.attribute("style"))
|
||||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
title = request.child('title')
|
title = request.child("title")
|
||||||
title_gr = request.child('title_gr')
|
title_gr = request.child("title_gr")
|
||||||
titledict = newprofile.get_dict('title')
|
titledict = newprofile.get_dict("title")
|
||||||
title_grdict = newprofile.get_dict('title_gr')
|
title_grdict = newprofile.get_dict("title_gr")
|
||||||
|
|
||||||
# Groove radar level ups
|
# 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
|
# Set the correct values depending on if we're single or double play
|
||||||
if is_dp:
|
if is_dp:
|
||||||
play_stats.increment_int('double_plays')
|
play_stats.increment_int("double_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_d',
|
"gr_d",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('d', title.value)
|
titledict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('d', title.value)
|
title_grdict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
else:
|
else:
|
||||||
play_stats.increment_int('single_plays')
|
play_stats.increment_int("single_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_s',
|
"gr_s",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('s', title.value)
|
titledict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('s', title.value)
|
title_grdict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
play_stats.increment_int(f'cnt_m{mode}')
|
play_stats.increment_int(f"cnt_m{mode}")
|
||||||
|
|
||||||
# Result stars
|
# Result stars
|
||||||
result_star = request.child('result_star')
|
result_star = request.child("result_star")
|
||||||
if result_star is not None:
|
if result_star is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'result_stars',
|
"result_stars",
|
||||||
9,
|
9,
|
||||||
[
|
[
|
||||||
intish(result_star.attribute('slot1')),
|
intish(result_star.attribute("slot1")),
|
||||||
intish(result_star.attribute('slot2')),
|
intish(result_star.attribute("slot2")),
|
||||||
intish(result_star.attribute('slot3')),
|
intish(result_star.attribute("slot3")),
|
||||||
intish(result_star.attribute('slot4')),
|
intish(result_star.attribute("slot4")),
|
||||||
intish(result_star.attribute('slot5')),
|
intish(result_star.attribute("slot5")),
|
||||||
intish(result_star.attribute('slot6')),
|
intish(result_star.attribute("slot6")),
|
||||||
intish(result_star.attribute('slot7')),
|
intish(result_star.attribute("slot7")),
|
||||||
intish(result_star.attribute('slot8')),
|
intish(result_star.attribute("slot8")),
|
||||||
intish(result_star.attribute('slot9')),
|
intish(result_star.attribute("slot9")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Target stuff
|
# Target stuff
|
||||||
target = request.child('target')
|
target = request.child("target")
|
||||||
if target is not None:
|
if target is not None:
|
||||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||||
|
|
||||||
# Update last attributes
|
# Update last attributes
|
||||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Grab character options
|
# Grab character options
|
||||||
chara = request.child('chara')
|
chara = request.child("chara")
|
||||||
if chara is not None:
|
if chara is not None:
|
||||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||||
chara_opt = request.child('chara_opt')
|
chara_opt = request.child("chara_opt")
|
||||||
if chara_opt is not None:
|
if chara_opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Options
|
||||||
option_02 = request.child('option_02')
|
option_02 = request.child("option_02")
|
||||||
if option_02 is not None:
|
if option_02 is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Experience and stars
|
||||||
exp = request.child_value('exp')
|
exp = request.child_value("exp")
|
||||||
if exp is not None:
|
if exp is not None:
|
||||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||||
star = request.child_value('star')
|
star = request.child_value("star")
|
||||||
if star is not None:
|
if star is not None:
|
||||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||||
star_c = request.child_value('star_c')
|
star_c = request.child_value("star_c")
|
||||||
if star_c is not None:
|
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
|
# Update game flags
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name not in ['flag', 'flag_ex']:
|
if child.name not in ["flag", "flag_ex"]:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
value = int(child.attribute('data'))
|
value = int(child.attribute("data"))
|
||||||
offset = int(child.attribute('no'))
|
offset = int(child.attribute("no"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
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):
|
if offset < 0 or offset >= len(flags):
|
||||||
continue
|
continue
|
||||||
flags[offset] = value
|
flags[offset] = value
|
||||||
newprofile.replace_int_array('flag', 512, flags)
|
newprofile.replace_int_array("flag", 512, flags)
|
||||||
|
|
||||||
# Workout mode support
|
# Workout mode support
|
||||||
newweight = -1
|
newweight = -1
|
||||||
oldweight = newprofile.get_int('weight')
|
oldweight = newprofile.get_int("weight")
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'weight':
|
if child.name != "weight":
|
||||||
continue
|
continue
|
||||||
newweight = child.value
|
newweight = child.value
|
||||||
if newweight < 0:
|
if newweight < 0:
|
||||||
@ -686,14 +714,14 @@ class DDR2014(
|
|||||||
# Either update or unset the weight depending on the game
|
# Either update or unset the weight depending on the game
|
||||||
if newweight == 0:
|
if newweight == 0:
|
||||||
# Weight is unset or we declined to use this feature, remove from profile
|
# Weight is unset or we declined to use this feature, remove from profile
|
||||||
if 'weight' in newprofile:
|
if "weight" in newprofile:
|
||||||
del newprofile['weight']
|
del newprofile["weight"]
|
||||||
else:
|
else:
|
||||||
# Weight has been set or previously retrieved, we should save calories
|
# Weight has been set or previously retrieved, we should save calories
|
||||||
newprofile.replace_int('weight', newweight)
|
newprofile.replace_int("weight", newweight)
|
||||||
total = 0
|
total = 0
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'calory':
|
if child.name != "calory":
|
||||||
continue
|
continue
|
||||||
total += child.value
|
total += child.value
|
||||||
self.data.local.user.put_time_based_achievement(
|
self.data.local.user.put_time_based_achievement(
|
||||||
@ -701,10 +729,10 @@ class DDR2014(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
0,
|
0,
|
||||||
'workout',
|
"workout",
|
||||||
{
|
{
|
||||||
'calories': total,
|
"calories": total,
|
||||||
'weight': newweight,
|
"weight": newweight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -712,7 +740,7 @@ class DDR2014(
|
|||||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
@ -721,11 +749,11 @@ class DDR2014(
|
|||||||
# Save any rivals that were added/removed/changed
|
# Save any rivals that were added/removed/changed
|
||||||
newfriends = oldfriends[:]
|
newfriends = oldfriends[:]
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'friend':
|
if child.name != "friend":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
code = int(child.attribute('code'))
|
code = int(child.attribute("code"))
|
||||||
pos = int(child.attribute('pos'))
|
pos = int(child.attribute("pos"))
|
||||||
|
|
||||||
if pos >= 0 and pos < 10:
|
if pos >= 0 and pos < 10:
|
||||||
if code == 0:
|
if code == 0:
|
||||||
@ -733,7 +761,9 @@ class DDR2014(
|
|||||||
newfriends[pos] = None
|
newfriends[pos] = None
|
||||||
else:
|
else:
|
||||||
# Try looking up the userid
|
# 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
|
# Diff the set of links to determine updates
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
@ -746,7 +776,7 @@ class DDR2014(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
elif oldfriends[i] is None:
|
elif oldfriends[i] is None:
|
||||||
@ -755,7 +785,7 @@ class DDR2014(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
@ -765,24 +795,24 @@ class DDR2014(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
self.data.local.user.put_link(
|
self.data.local.user.put_link(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Play area counter
|
# 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:
|
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
|
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
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid, play_stats)
|
self.update_play_statistics(userid, play_stats)
|
||||||
|
@ -10,7 +10,7 @@ class DDRA20(
|
|||||||
DDRBase,
|
DDRBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution A20'
|
name: str = "DanceDanceRevolution A20"
|
||||||
version: int = VersionConstants.DDR_A20
|
version: int = VersionConstants.DDR_A20
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -18,7 +18,7 @@ class DDRA20(
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def supports_paseli(self) -> bool:
|
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.
|
# DDR Ace in USA mode doesn't support PASELI properly.
|
||||||
# When in Asia mode it shows PASELI but won't let you select it.
|
# When in Asia mode it shows PASELI but won't let you select it.
|
||||||
return False
|
return False
|
||||||
|
@ -6,7 +6,15 @@ from typing_extensions import Final
|
|||||||
from bemani.backend.ess import EventLogHandler
|
from bemani.backend.ess import EventLogHandler
|
||||||
from bemani.backend.ddr.base import DDRBase
|
from bemani.backend.ddr.base import DDRBase
|
||||||
from bemani.backend.ddr.ddr2014 import DDR2014
|
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.data import Data, Achievement, Machine, Score, UserID
|
||||||
from bemani.protocol import Node
|
from bemani.protocol import Node
|
||||||
|
|
||||||
@ -16,7 +24,7 @@ class DDRAce(
|
|||||||
EventLogHandler,
|
EventLogHandler,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution A'
|
name: str = "DanceDanceRevolution A"
|
||||||
version: int = VersionConstants.DDR_ACE
|
version: int = VersionConstants.DDR_ACE
|
||||||
|
|
||||||
GAME_STYLE_SINGLE: Final[int] = 0
|
GAME_STYLE_SINGLE: Final[int] = 0
|
||||||
@ -106,7 +114,9 @@ class DDRAce(
|
|||||||
return DDR2014(self.data, self.config, self.model)
|
return DDR2014(self.data, self.config, self.model)
|
||||||
|
|
||||||
@classmethod
|
@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
|
# 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
|
# 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
|
# is no profile on read-back, it errors out, and it also uses the name
|
||||||
@ -125,20 +135,25 @@ class DDRAce(
|
|||||||
events = []
|
events = []
|
||||||
|
|
||||||
for userid, profile in profiles:
|
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)
|
data.local.user.delete_profile(cls.game, cls.version, userid)
|
||||||
events.append((
|
events.append(
|
||||||
'ddr_profile_purge',
|
(
|
||||||
|
"ddr_profile_purge",
|
||||||
{
|
{
|
||||||
'userid': userid,
|
"userid": userid,
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return events
|
return events
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supports_paseli(self) -> bool:
|
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.
|
# DDR Ace in USA mode doesn't support PASELI properly.
|
||||||
# When in Asia mode it shows PASELI but won't let you select it.
|
# When in Asia mode it shows PASELI but won't let you select it.
|
||||||
return False
|
return False
|
||||||
@ -237,19 +252,25 @@ class DDRAce(
|
|||||||
return self.GAME_HALO_NONE
|
return self.GAME_HALO_NONE
|
||||||
|
|
||||||
def handle_tax_get_phase_request(self, request: Node) -> Node:
|
def handle_tax_get_phase_request(self, request: Node) -> Node:
|
||||||
tax = Node.void('tax')
|
tax = Node.void("tax")
|
||||||
tax.add_child(Node.s32('phase', 0))
|
tax.add_child(Node.s32("phase", 0))
|
||||||
return tax
|
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
|
has_profile: bool = False
|
||||||
achievements: List[Achievement] = []
|
achievements: List[Achievement] = []
|
||||||
scores: List[Score] = []
|
scores: List[Score] = []
|
||||||
|
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
has_profile = self.has_profile(userid)
|
has_profile = self.has_profile(userid)
|
||||||
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
|
achievements = self.data.local.user.get_achievements(
|
||||||
scores = self.data.remote.music.get_scores(self.game, self.music_version, userid)
|
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.
|
# Place scores into an arrangement for easier distribution to Ace.
|
||||||
scores_by_mcode: Dict[int, List[Optional[Score]]] = {}
|
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
|
scores_by_mcode[score.id][self.db_to_game_chart(score.chart)] = score
|
||||||
|
|
||||||
# First, set new flag
|
# 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
|
# Now, return the scores to Ace
|
||||||
for mcode in scores_by_mcode:
|
for mcode in scores_by_mcode:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
response.add_child(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]
|
scores_that_matter = scores_by_mcode[mcode]
|
||||||
while scores_that_matter[-1] is None:
|
while scores_that_matter[-1] is None:
|
||||||
scores_that_matter = scores_that_matter[:-1]
|
scores_that_matter = scores_that_matter[:-1]
|
||||||
|
|
||||||
for score in scores_that_matter:
|
for score in scores_that_matter:
|
||||||
note = Node.void('note')
|
note = Node.void("note")
|
||||||
music.add_child(note)
|
music.add_child(note)
|
||||||
|
|
||||||
if score is None:
|
if score is None:
|
||||||
note.add_child(Node.u16('count', 0))
|
note.add_child(Node.u16("count", 0))
|
||||||
note.add_child(Node.u8('rank', 0))
|
note.add_child(Node.u8("rank", 0))
|
||||||
note.add_child(Node.u8('clearkind', 0))
|
note.add_child(Node.u8("clearkind", 0))
|
||||||
note.add_child(Node.s32('score', 0))
|
note.add_child(Node.s32("score", 0))
|
||||||
note.add_child(Node.s32('ghostid', 0))
|
note.add_child(Node.s32("ghostid", 0))
|
||||||
else:
|
else:
|
||||||
note.add_child(Node.u16('count', score.plays))
|
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(
|
||||||
note.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo'))))
|
Node.u8(
|
||||||
note.add_child(Node.s32('score', score.points))
|
"rank", self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
note.add_child(Node.s32('ghostid', score.key))
|
)
|
||||||
|
)
|
||||||
|
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
|
# Active event settings
|
||||||
activeevents = [
|
activeevents = [
|
||||||
@ -331,69 +361,83 @@ class DDRAce(
|
|||||||
|
|
||||||
# Event reward settings
|
# Event reward settings
|
||||||
rewards = {
|
rewards = {
|
||||||
'30': {
|
"30": {
|
||||||
999: 5,
|
999: 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Now handle event progress and activation.
|
# Now handle event progress and activation.
|
||||||
events = {ach.id: ach.data 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']
|
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
|
# Make sure we always send a babylon's adventure save event or the game won't send progress
|
||||||
babylon_included = False
|
babylon_included = False
|
||||||
for evtprogress in progress:
|
for evtprogress in progress:
|
||||||
if evtprogress.id == 999 and evtprogress.type == '30':
|
if evtprogress.id == 999 and evtprogress.type == "30":
|
||||||
babylon_included = True
|
babylon_included = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if not babylon_included:
|
if not babylon_included:
|
||||||
progress.append(Achievement(
|
progress.append(
|
||||||
|
Achievement(
|
||||||
999,
|
999,
|
||||||
'30',
|
"30",
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
'completed': False,
|
"completed": False,
|
||||||
'progress': 0,
|
"progress": 0,
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for event in activeevents:
|
for event in activeevents:
|
||||||
# Get completion data
|
# Get completion data
|
||||||
playerstats = events.get(event, ValidatedDict({'completed': False}))
|
playerstats = events.get(event, ValidatedDict({"completed": False}))
|
||||||
|
|
||||||
# Return the data
|
# Return the data
|
||||||
eventdata = Node.void('eventdata')
|
eventdata = Node.void("eventdata")
|
||||||
response.add_child(eventdata)
|
response.add_child(eventdata)
|
||||||
eventdata.add_child(Node.u32('eventid', event))
|
eventdata.add_child(Node.u32("eventid", event))
|
||||||
eventdata.add_child(Node.s32('eventtype', 9999))
|
eventdata.add_child(Node.s32("eventtype", 9999))
|
||||||
eventdata.add_child(Node.u32('eventno', 0))
|
eventdata.add_child(Node.u32("eventno", 0))
|
||||||
eventdata.add_child(Node.s64('condition', 0))
|
eventdata.add_child(Node.s64("condition", 0))
|
||||||
eventdata.add_child(Node.u32('reward', 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(
|
||||||
eventdata.add_child(Node.s64('savedata', 0))
|
Node.s32("comptime", 1 if playerstats.get_bool("completed") else 0)
|
||||||
|
)
|
||||||
|
eventdata.add_child(Node.s64("savedata", 0))
|
||||||
|
|
||||||
for evtprogress in progress:
|
for evtprogress in progress:
|
||||||
# Babylon's adventure progres and anything else the game sends
|
# Babylon's adventure progres and anything else the game sends
|
||||||
eventdata = Node.void('eventdata')
|
eventdata = Node.void("eventdata")
|
||||||
response.add_child(eventdata)
|
response.add_child(eventdata)
|
||||||
eventdata.add_child(Node.u32('eventid', evtprogress.id))
|
eventdata.add_child(Node.u32("eventid", evtprogress.id))
|
||||||
eventdata.add_child(Node.s32('eventtype', int(evtprogress.type)))
|
eventdata.add_child(Node.s32("eventtype", int(evtprogress.type)))
|
||||||
eventdata.add_child(Node.u32('eventno', 0))
|
eventdata.add_child(Node.u32("eventno", 0))
|
||||||
eventdata.add_child(Node.s64('condition', 0))
|
eventdata.add_child(Node.s64("condition", 0))
|
||||||
eventdata.add_child(Node.u32('reward', rewards.get(evtprogress.type, {}).get(evtprogress.id)))
|
eventdata.add_child(
|
||||||
eventdata.add_child(Node.s32('comptime', 1 if evtprogress.data.get_bool('completed') else 0))
|
Node.u32(
|
||||||
eventdata.add_child(Node.s64('savedata', evtprogress.data.get_int('progress')))
|
"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:
|
if userid is None:
|
||||||
# the game sends us empty user ID strings when a guest is playing.
|
# 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
|
# Return early so it doesn't wait a minute and a half to show the
|
||||||
# results screen.
|
# results screen.
|
||||||
return
|
return
|
||||||
|
|
||||||
if requestdata.child_value('isgameover'):
|
if requestdata.child_value("isgameover"):
|
||||||
style = int(requestdata.child_value('playstyle'))
|
style = int(requestdata.child_value("playstyle"))
|
||||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||||
|
|
||||||
# We don't save anything for gameover requests, since we
|
# 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
|
# as a spot to bump play counts and such
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
if is_dp:
|
if is_dp:
|
||||||
play_stats.increment_int('double_plays')
|
play_stats.increment_int("double_plays")
|
||||||
else:
|
else:
|
||||||
play_stats.increment_int('single_plays')
|
play_stats.increment_int("single_plays")
|
||||||
self.update_play_statistics(userid, play_stats)
|
self.update_play_statistics(userid, play_stats)
|
||||||
|
|
||||||
# Now is a good time to check if we have workout mode enabled,
|
# Now is a good time to check if we have workout mode enabled,
|
||||||
# and if so, store the calories earned for this set.
|
# and if so, store the calories earned for this set.
|
||||||
profile = self.get_profile(userid)
|
profile = self.get_profile(userid)
|
||||||
enabled = profile.get_bool('workout_mode')
|
enabled = profile.get_bool("workout_mode")
|
||||||
weight = profile.get_int('weight')
|
weight = profile.get_int("weight")
|
||||||
|
|
||||||
if enabled and weight > 0:
|
if enabled and weight > 0:
|
||||||
# We enabled weight display, find the calories and save them
|
# We enabled weight display, find the calories and save them
|
||||||
total = 0
|
total = 0
|
||||||
for child in requestdata.children:
|
for child in requestdata.children:
|
||||||
if child.name != 'note':
|
if child.name != "note":
|
||||||
continue
|
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.data.local.user.put_time_based_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
0,
|
0,
|
||||||
'workout',
|
"workout",
|
||||||
{
|
{
|
||||||
'calories': total,
|
"calories": total,
|
||||||
'weight': weight,
|
"weight": weight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find any event updates
|
# Find any event updates
|
||||||
for child in requestdata.children:
|
for child in requestdata.children:
|
||||||
if child.name != 'event':
|
if child.name != "event":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip empty events or events we don't support
|
# Skip empty events or events we don't support
|
||||||
eventid = child.child_value('eventid')
|
eventid = child.child_value("eventid")
|
||||||
eventtype = child.child_value('eventtype')
|
eventtype = child.child_value("eventtype")
|
||||||
if eventid == 0 or eventtype == 0:
|
if eventid == 0 or eventtype == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Save data to replay to the client later
|
# Save data to replay to the client later
|
||||||
completed = child.child_value('comptime') != 0
|
completed = child.child_value("comptime") != 0
|
||||||
progress = child.child_value('savedata')
|
progress = child.child_value("savedata")
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
@ -455,8 +499,8 @@ class DDRAce(
|
|||||||
eventid,
|
eventid,
|
||||||
str(eventtype),
|
str(eventtype),
|
||||||
{
|
{
|
||||||
'completed': completed,
|
"completed": completed,
|
||||||
'progress': progress,
|
"progress": progress,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -466,23 +510,23 @@ class DDRAce(
|
|||||||
score = None
|
score = None
|
||||||
stagenum = 0
|
stagenum = 0
|
||||||
for child in requestdata.children:
|
for child in requestdata.children:
|
||||||
if child.name != 'note':
|
if child.name != "note":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if child.child_value('stagenum') > stagenum:
|
if child.child_value("stagenum") > stagenum:
|
||||||
score = child
|
score = child
|
||||||
stagenum = child.child_value('stagenum')
|
stagenum = child.child_value("stagenum")
|
||||||
|
|
||||||
if score is None:
|
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')
|
songid = score.child_value("mcode")
|
||||||
chart = self.game_to_db_chart(score.child_value('notetype'))
|
chart = self.game_to_db_chart(score.child_value("notetype"))
|
||||||
rank = self.game_to_db_rank(score.child_value('rank'))
|
rank = self.game_to_db_rank(score.child_value("rank"))
|
||||||
halo = self.game_to_db_halo(score.child_value('clearkind'))
|
halo = self.game_to_db_halo(score.child_value("clearkind"))
|
||||||
points = score.child_value('score')
|
points = score.child_value("score")
|
||||||
combo = score.child_value('maxcombo')
|
combo = score.child_value("maxcombo")
|
||||||
ghost = score.child_value('ghost')
|
ghost = score.child_value("ghost")
|
||||||
self.update_score(
|
self.update_score(
|
||||||
userid,
|
userid,
|
||||||
songid,
|
songid,
|
||||||
@ -494,15 +538,17 @@ class DDRAce(
|
|||||||
ghost=ghost,
|
ghost=ghost,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __handle_rivalload(self, userid: Optional[UserID], requestdata: Node, response: Node) -> None:
|
def __handle_rivalload(
|
||||||
data = Node.void('data')
|
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||||
|
) -> None:
|
||||||
|
data = Node.void("data")
|
||||||
response.add_child(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)
|
thismachine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||||
machines_by_id: Dict[int, Optional[Machine]] = {thismachine.id: thismachine}
|
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] = {}
|
profiles_by_userid: Dict[UserID, Profile] = {}
|
||||||
|
|
||||||
def get_machine(lid: int) -> Optional[Machine]:
|
def get_machine(lid: int) -> Optional[Machine]:
|
||||||
@ -522,7 +568,9 @@ class DDRAce(
|
|||||||
|
|
||||||
if loadkind == self.GAME_RIVAL_TYPE_WORLD:
|
if loadkind == self.GAME_RIVAL_TYPE_WORLD:
|
||||||
# Just load all scores for this network
|
# 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:
|
elif loadkind == self.GAME_RIVAL_TYPE_AREA:
|
||||||
if thismachine.arcade is not None:
|
if thismachine.arcade is not None:
|
||||||
match_arcade = thismachine.arcade
|
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 have an arcade to match, see if this user's location matches the arcade.
|
||||||
# If we don't, just match lid directly
|
# If we don't, just match lid directly
|
||||||
if match_arcade is not None:
|
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:
|
if theirmachine is not None and theirmachine.arcade == match_arcade:
|
||||||
userids.append(userid)
|
userids.append(userid)
|
||||||
elif match_machine is not None:
|
elif match_machine is not None:
|
||||||
if profiledata.get_int('lid') == match_machine:
|
if profiledata.get_int("lid") == match_machine:
|
||||||
userids.append(userid)
|
userids.append(userid)
|
||||||
|
|
||||||
# Load all scores for users in the area
|
# 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:
|
elif loadkind == self.GAME_RIVAL_TYPE_MACHINE:
|
||||||
# Load up all scores and filter them by those earned at this location
|
# 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 [
|
elif loadkind in [
|
||||||
self.GAME_RIVAL_TYPE_RIVAL1,
|
self.GAME_RIVAL_TYPE_RIVAL1,
|
||||||
self.GAME_RIVAL_TYPE_RIVAL2,
|
self.GAME_RIVAL_TYPE_RIVAL2,
|
||||||
self.GAME_RIVAL_TYPE_RIVAL3,
|
self.GAME_RIVAL_TYPE_RIVAL3,
|
||||||
]:
|
]:
|
||||||
# Load up this user's highscores, format the way the below code expects it
|
# 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)
|
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]
|
scores = [(otherid, score) for score in userscores]
|
||||||
else:
|
else:
|
||||||
# Nothing here
|
# Nothing here
|
||||||
scores = []
|
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):
|
for (userid, profile) in self.get_any_profiles(missing_users):
|
||||||
profiles_by_userid[userid] = profile
|
profiles_by_userid[userid] = profile
|
||||||
|
|
||||||
for userid, score in scores:
|
for userid, score in scores:
|
||||||
if profiles_by_userid.get(userid) is None:
|
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]
|
profiledata = profiles_by_userid[userid]
|
||||||
|
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
data.add_child(record)
|
data.add_child(record)
|
||||||
record.add_child(Node.u32('mcode', score.id))
|
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("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(
|
||||||
record.add_child(Node.u8('clearkind', self.db_to_game_halo(score.data.get_int('halo'))))
|
Node.u8("rank", self.db_to_game_rank(score.data.get_int("rank")))
|
||||||
record.add_child(Node.u8('flagdata', 0))
|
)
|
||||||
record.add_child(Node.string('name', profiledata.get_str('name')))
|
record.add_child(
|
||||||
record.add_child(Node.s32('area', profiledata.get_int('area', 58)))
|
Node.u8("clearkind", self.db_to_game_halo(score.data.get_int("halo")))
|
||||||
record.add_child(Node.s32('code', profiledata.extid))
|
)
|
||||||
record.add_child(Node.s32('score', score.points))
|
record.add_child(Node.u8("flagdata", 0))
|
||||||
record.add_child(Node.s32('ghostid', score.key))
|
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:
|
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)
|
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||||
profile = Profile(
|
profile = Profile(
|
||||||
@ -599,27 +661,35 @@ class DDRAce(
|
|||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'lid': machine.id,
|
"lid": machine.id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.put_profile(userid, profile)
|
self.put_profile(userid, profile)
|
||||||
|
|
||||||
response.add_child(Node.string('seq', ID.format_extid(profile.extid)))
|
response.add_child(Node.string("seq", ID.format_extid(profile.extid)))
|
||||||
response.add_child(Node.s32('code', profile.extid))
|
response.add_child(Node.s32("code", profile.extid))
|
||||||
response.add_child(Node.string('shoparea', ''))
|
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:
|
if userid is not None:
|
||||||
previous_version = self.previous_version()
|
previous_version = self.previous_version()
|
||||||
profile = previous_version.get_profile(userid)
|
profile = previous_version.get_profile(userid)
|
||||||
else:
|
else:
|
||||||
profile = None
|
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:
|
def __handle_ghostload(
|
||||||
ghostid = requestdata.child_value('ghostid')
|
self, userid: Optional[UserID], requestdata: Node, response: Node
|
||||||
ghost = self.data.local.music.get_score_by_key(self.game, self.music_version, ghostid)
|
) -> 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:
|
if ghost is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -628,110 +698,188 @@ class DDRAce(
|
|||||||
if profile is None:
|
if profile is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'ghost' not in score.data:
|
if "ghost" not in score.data:
|
||||||
return
|
return
|
||||||
|
|
||||||
ghostdata = Node.void('ghostdata')
|
ghostdata = Node.void("ghostdata")
|
||||||
response.add_child(ghostdata)
|
response.add_child(ghostdata)
|
||||||
ghostdata.add_child(Node.s32('code', profile.extid))
|
ghostdata.add_child(Node.s32("code", profile.extid))
|
||||||
ghostdata.add_child(Node.u32('mcode', score.id))
|
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.u8("notetype", self.db_to_game_chart(score.chart)))
|
||||||
ghostdata.add_child(Node.s32('ghostsize', len(score.data['ghost'])))
|
ghostdata.add_child(Node.s32("ghostsize", len(score.data["ghost"])))
|
||||||
ghostdata.add_child(Node.string('ghost', score.data['ghost']))
|
ghostdata.add_child(Node.string("ghost", score.data["ghost"]))
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_advanced_request(self, request: Node) -> Optional[Node]:
|
def handle_playerdata_usergamedata_advanced_request(
|
||||||
playerdata = Node.void('playerdata')
|
self, request: Node
|
||||||
|
) -> Optional[Node]:
|
||||||
|
playerdata = Node.void("playerdata")
|
||||||
|
|
||||||
# DDR Ace decides to be difficult and have a third level of packet switching
|
# DDR Ace decides to be difficult and have a third level of packet switching
|
||||||
mode = request.child_value('data/mode')
|
mode = request.child_value("data/mode")
|
||||||
refid = request.child_value('data/refid')
|
refid = request.child_value("data/refid")
|
||||||
extid = request.child_value('data/ddrcode')
|
extid = request.child_value("data/ddrcode")
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
# Possibly look up by extid instead
|
# Possibly look up by extid instead
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
|
|
||||||
if mode == 'userload':
|
if mode == "userload":
|
||||||
self.__handle_userload(userid, request.child('data'), playerdata)
|
self.__handle_userload(userid, request.child("data"), playerdata)
|
||||||
elif mode == 'usersave':
|
elif mode == "usersave":
|
||||||
self.__handle_usersave(userid, request.child('data'), playerdata)
|
self.__handle_usersave(userid, request.child("data"), playerdata)
|
||||||
elif mode == 'rivalload':
|
elif mode == "rivalload":
|
||||||
self.__handle_rivalload(userid, request.child('data'), playerdata)
|
self.__handle_rivalload(userid, request.child("data"), playerdata)
|
||||||
elif mode == 'usernew':
|
elif mode == "usernew":
|
||||||
self.__handle_usernew(userid, request.child('data'), playerdata)
|
self.__handle_usernew(userid, request.child("data"), playerdata)
|
||||||
elif mode == 'inheritance':
|
elif mode == "inheritance":
|
||||||
self.__handle_inheritance(userid, request.child('data'), playerdata)
|
self.__handle_inheritance(userid, request.child("data"), playerdata)
|
||||||
elif mode == 'ghostload':
|
elif mode == "ghostload":
|
||||||
self.__handle_ghostload(userid, request.child('data'), playerdata)
|
self.__handle_ghostload(userid, request.child("data"), playerdata)
|
||||||
else:
|
else:
|
||||||
# We don't support this
|
# We don't support this
|
||||||
return None
|
return None
|
||||||
|
|
||||||
playerdata.add_child(Node.s32('result', 0))
|
playerdata.add_child(Node.s32("result", 0))
|
||||||
return playerdata
|
return playerdata
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||||
playerdata = Node.void('playerdata')
|
playerdata = Node.void("playerdata")
|
||||||
refid = request.child_value('data/refid')
|
refid = request.child_value("data/refid")
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
profile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
profile = self.get_profile(userid) or Profile(
|
||||||
usergamedata = profile.get_dict('usergamedata')
|
self.game, self.version, refid, 0
|
||||||
|
)
|
||||||
|
usergamedata = profile.get_dict("usergamedata")
|
||||||
|
|
||||||
for record in request.child('data/record').children:
|
for record in request.child("data/record").children:
|
||||||
if record.name != 'd':
|
if record.name != "d":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
strdata = base64.b64decode(record.value)
|
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
|
# Grab and format the profile objects
|
||||||
strdatalist = strdata.split(b',')
|
strdatalist = strdata.split(b",")
|
||||||
profiletype = strdatalist[1].decode('utf-8')
|
profiletype = strdatalist[1].decode("utf-8")
|
||||||
strdatalist = strdatalist[2:]
|
strdatalist = strdatalist[2:]
|
||||||
|
|
||||||
# Extract relevant bits for frontend/API
|
# Extract relevant bits for frontend/API
|
||||||
if profiletype == 'COMMON':
|
if profiletype == "COMMON":
|
||||||
profile.replace_str('name', strdatalist[self.GAME_COMMON_NAME_OFFSET].decode('ascii'))
|
profile.replace_str(
|
||||||
profile.replace_int('area', intish(strdatalist[self.GAME_COMMON_AREA_OFFSET].decode('ascii'), 16))
|
"name",
|
||||||
profile.replace_bool('workout_mode', int(strdatalist[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET].decode('ascii'), 16) != 0)
|
strdatalist[self.GAME_COMMON_NAME_OFFSET].decode("ascii"),
|
||||||
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))
|
profile.replace_int(
|
||||||
if profiletype == 'OPTION':
|
"area",
|
||||||
profile.replace_int('combo', int(strdatalist[self.GAME_OPTION_COMBO_POSITION_OFFSET].decode('ascii'), 16))
|
intish(
|
||||||
profile.replace_int('early_late', int(strdatalist[self.GAME_OPTION_FAST_SLOW_OFFSET].decode('ascii'), 16))
|
strdatalist[self.GAME_COMMON_AREA_OFFSET].decode("ascii"),
|
||||||
profile.replace_int('arrowskin', int(strdatalist[self.GAME_OPTION_ARROW_SKIN_OFFSET].decode('ascii'), 16))
|
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))
|
)
|
||||||
|
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] = {
|
usergamedata[profiletype] = {
|
||||||
'strdata': b','.join(strdatalist),
|
"strdata": b",".join(strdatalist),
|
||||||
'bindata': bindata,
|
"bindata": bindata,
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.replace_dict('usergamedata', usergamedata)
|
profile.replace_dict("usergamedata", usergamedata)
|
||||||
profile.replace_int('write_time', Time.now())
|
profile.replace_int("write_time", Time.now())
|
||||||
self.put_profile(userid, profile)
|
self.put_profile(userid, profile)
|
||||||
|
|
||||||
playerdata.add_child(Node.s32('result', 0))
|
playerdata.add_child(Node.s32("result", 0))
|
||||||
return playerdata
|
return playerdata
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
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)
|
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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
profile = self.get_profile(userid)
|
profile = self.get_profile(userid)
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
records = 0
|
records = 0
|
||||||
|
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
player.add_child(record)
|
player.add_child(record)
|
||||||
|
|
||||||
def acehex(val: int) -> str:
|
def acehex(val: int) -> str:
|
||||||
@ -739,70 +887,92 @@ class DDRAce(
|
|||||||
|
|
||||||
if profile is None:
|
if profile is None:
|
||||||
# Just return a default empty node
|
# Just return a default empty node
|
||||||
record.add_child(Node.string('d', '<NODATA>'))
|
record.add_child(Node.string("d", "<NODATA>"))
|
||||||
records = 1
|
records = 1
|
||||||
else:
|
else:
|
||||||
# Figure out what profiles are being requested
|
# Figure out what profiles are being requested
|
||||||
profiletypes = request.child_value('data/recv_csv').split(',')[::2]
|
profiletypes = request.child_value("data/recv_csv").split(",")[::2]
|
||||||
usergamedata = profile.get_dict('usergamedata')
|
usergamedata = profile.get_dict("usergamedata")
|
||||||
for ptype in profiletypes:
|
for ptype in profiletypes:
|
||||||
if ptype in usergamedata:
|
if ptype in usergamedata:
|
||||||
records = records + 1
|
records = records + 1
|
||||||
|
|
||||||
if ptype == "COMMON":
|
if ptype == "COMMON":
|
||||||
# Return basic profile options
|
# Return basic profile options
|
||||||
name = profile.get_str('name')
|
name = profile.get_str("name")
|
||||||
area = profile.get_int('area', self.get_machine_region())
|
area = profile.get_int("area", self.get_machine_region())
|
||||||
if name == "":
|
if name == "":
|
||||||
# This is a bogus profile created by the first login, substitute the
|
# This is a bogus profile created by the first login, substitute the
|
||||||
# previous version values so that profile succession works.
|
# previous version values so that profile succession works.
|
||||||
previous_version = self.previous_version()
|
previous_version = self.previous_version()
|
||||||
old_profile = previous_version.get_profile(userid)
|
old_profile = previous_version.get_profile(userid)
|
||||||
if old_profile is not None:
|
if old_profile is not None:
|
||||||
name = old_profile.get_str('name')
|
name = old_profile.get_str("name")
|
||||||
area = old_profile.get_int('area', self.get_machine_region())
|
area = old_profile.get_int(
|
||||||
|
"area", self.get_machine_region()
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
area = self.get_machine_region()
|
area = self.get_machine_region()
|
||||||
|
|
||||||
common = usergamedata[ptype]['strdata'].split(b',')
|
common = usergamedata[ptype]["strdata"].split(b",")
|
||||||
common[self.GAME_COMMON_NAME_OFFSET] = name.encode('ascii')
|
common[self.GAME_COMMON_NAME_OFFSET] = name.encode("ascii")
|
||||||
common[self.GAME_COMMON_AREA_OFFSET] = acehex(area).encode('ascii')
|
common[self.GAME_COMMON_AREA_OFFSET] = acehex(area).encode(
|
||||||
common[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET] = b'1' if profile.get_bool('workout_mode') else b'0'
|
"ascii"
|
||||||
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')
|
common[self.GAME_COMMON_WEIGHT_DISPLAY_OFFSET] = (
|
||||||
usergamedata[ptype]['strdata'] = b','.join(common)
|
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":
|
if ptype == "OPTION":
|
||||||
# Return user settings for frontend
|
# Return user settings for frontend
|
||||||
option = usergamedata[ptype]['strdata'].split(b',')
|
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_FAST_SLOW_OFFSET] = acehex(
|
||||||
option[self.GAME_OPTION_COMBO_POSITION_OFFSET] = acehex(profile.get_int('combo')).encode('ascii')
|
profile.get_int("early_late")
|
||||||
option[self.GAME_OPTION_ARROW_SKIN_OFFSET] = acehex(profile.get_int('arrowskin')).encode('ascii')
|
).encode("ascii")
|
||||||
option[self.GAME_OPTION_GUIDELINE_OFFSET] = acehex(profile.get_int('guidelines')).encode('ascii')
|
option[self.GAME_OPTION_COMBO_POSITION_OFFSET] = acehex(
|
||||||
option[self.GAME_OPTION_FILTER_OFFSET] = acehex(profile.get_int('filter')).encode('ascii')
|
profile.get_int("combo")
|
||||||
usergamedata[ptype]['strdata'] = b','.join(option)
|
).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":
|
if ptype == "LAST":
|
||||||
# Return the number of calories expended in the last day
|
# Return the number of calories expended in the last day
|
||||||
workouts = self.data.local.user.get_time_based_achievements(
|
workouts = self.data.local.user.get_time_based_achievements(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
achievementtype='workout',
|
achievementtype="workout",
|
||||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
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 = usergamedata[ptype]["strdata"].split(b",")
|
||||||
last[self.GAME_LAST_CALORIES_OFFSET] = acehex(total).encode('ascii')
|
last[self.GAME_LAST_CALORIES_OFFSET] = acehex(total).encode(
|
||||||
usergamedata[ptype]['strdata'] = b','.join(last)
|
"ascii"
|
||||||
|
)
|
||||||
|
usergamedata[ptype]["strdata"] = b",".join(last)
|
||||||
if ptype == "RIVAL":
|
if ptype == "RIVAL":
|
||||||
# Fill in the DDR code and active status of the three active
|
# Fill in the DDR code and active status of the three active
|
||||||
# rivals.
|
# rivals.
|
||||||
rival = usergamedata[ptype]['strdata'].split(b',')
|
rival = usergamedata[ptype]["strdata"].split(b",")
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
|
|
||||||
friends: Dict[int, Optional[Profile]] = {}
|
friends: Dict[int, Optional[Profile]] = {}
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
@ -815,16 +985,16 @@ class DDRAce(
|
|||||||
3: self.GAME_RIVAL_SLOT_3_ACTIVE_OFFSET,
|
3: self.GAME_RIVAL_SLOT_3_ACTIVE_OFFSET,
|
||||||
}[rivalno]
|
}[rivalno]
|
||||||
|
|
||||||
whichfriend = lastdict.get_int(f'rival{rivalno}') - 1
|
whichfriend = lastdict.get_int(f"rival{rivalno}") - 1
|
||||||
if whichfriend < 0:
|
if whichfriend < 0:
|
||||||
# This rival isn't active
|
# This rival isn't active
|
||||||
rival[activeslot] = b'0'
|
rival[activeslot] = b"0"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
friendprofile = friends.get(whichfriend)
|
friendprofile = friends.get(whichfriend)
|
||||||
if friendprofile is None:
|
if friendprofile is None:
|
||||||
# This rival doesn't exist
|
# This rival doesn't exist
|
||||||
rival[activeslot] = b'0'
|
rival[activeslot] = b"0"
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ddrcodeslot = {
|
ddrcodeslot = {
|
||||||
@ -833,28 +1003,42 @@ class DDRAce(
|
|||||||
3: self.GAME_RIVAL_SLOT_3_DDRCODE_OFFSET,
|
3: self.GAME_RIVAL_SLOT_3_DDRCODE_OFFSET,
|
||||||
}[rivalno]
|
}[rivalno]
|
||||||
|
|
||||||
rival[activeslot] = acehex(rivalno).encode('ascii')
|
rival[activeslot] = acehex(rivalno).encode("ascii")
|
||||||
rival[ddrcodeslot] = acehex(friendprofile.extid).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 = Node.string(
|
||||||
dnode.add_child(Node.string('bin1', base64.b64encode(usergamedata[ptype]['bindata']).decode('ascii')))
|
"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)
|
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
|
return playerdata
|
||||||
|
|
||||||
def handle_system_convcardnumber_request(self, request: Node) -> Node:
|
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)
|
cardnumber = CardCipher.encode(cardid)
|
||||||
|
|
||||||
system = Node.void('system')
|
system = Node.void("system")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
system.add_child(data)
|
system.add_child(data)
|
||||||
|
|
||||||
system.add_child(Node.s32('result', 0))
|
system.add_child(Node.s32("result", 0))
|
||||||
data.add_child(Node.string('card_number', cardnumber))
|
data.add_child(Node.string("card_number", cardnumber))
|
||||||
return system
|
return system
|
||||||
|
@ -43,7 +43,7 @@ class DDRX2(
|
|||||||
DDRBase,
|
DDRBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution X2'
|
name: str = "DanceDanceRevolution X2"
|
||||||
version: int = VersionConstants.DDR_X2
|
version: int = VersionConstants.DDR_X2
|
||||||
|
|
||||||
GAME_STYLE_SINGLE: Final[int] = 0
|
GAME_STYLE_SINGLE: Final[int] = 0
|
||||||
@ -147,22 +147,24 @@ class DDRX2(
|
|||||||
return combo_type
|
return combo_type
|
||||||
|
|
||||||
def handle_game_common_request(self, request: Node) -> Node:
|
def handle_game_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for flagid in range(256):
|
for flagid in range(256):
|
||||||
flag = Node.void('flag')
|
flag = Node.void("flag")
|
||||||
game.add_child(flag)
|
game.add_child(flag)
|
||||||
|
|
||||||
flag.set_attribute('id', str(flagid))
|
flag.set_attribute("id", str(flagid))
|
||||||
flag.set_attribute('s2', '0')
|
flag.set_attribute("s2", "0")
|
||||||
flag.set_attribute('s1', '0')
|
flag.set_attribute("s1", "0")
|
||||||
flag.set_attribute('t', '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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
return game
|
||||||
|
|
||||||
@ -179,13 +181,16 @@ class DDRX2(
|
|||||||
sortedrecords[score.id] = {}
|
sortedrecords[score.id] = {}
|
||||||
sortedrecords[score.id][score.chart] = (userid, score)
|
sortedrecords[score.id][score.chart] = (userid, score)
|
||||||
missing_profiles.append(userid)
|
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:
|
for song in sortedrecords:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink_num', str(song))
|
music.set_attribute("reclink_num", str(song))
|
||||||
|
|
||||||
for chart in sortedrecords[song]:
|
for chart in sortedrecords[song]:
|
||||||
userid, score = sortedrecords[song][chart]
|
userid, score = sortedrecords[song][chart]
|
||||||
@ -194,24 +199,28 @@ class DDRX2(
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.string("name", users[userid].get_str("name")))
|
||||||
typenode.add_child(Node.u32('score', score.points))
|
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(
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
Node.u16(
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
"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
|
return game
|
||||||
|
|
||||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
# Rival score loading
|
# Rival score loading
|
||||||
@ -221,7 +230,9 @@ class DDRX2(
|
|||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
|
|
||||||
if userid is not 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:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
@ -231,11 +242,11 @@ class DDRX2(
|
|||||||
sortedscores[score.id] = {}
|
sortedscores[score.id] = {}
|
||||||
sortedscores[score.id][score.chart] = score
|
sortedscores[score.id][score.chart] = score
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for song in sortedscores:
|
for song in sortedscores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink', str(song))
|
music.set_attribute("reclink", str(song))
|
||||||
|
|
||||||
for chart in sortedscores[song]:
|
for chart in sortedscores[song]:
|
||||||
score = sortedscores[song][chart]
|
score = sortedscores[song][chart]
|
||||||
@ -244,39 +255,39 @@ class DDRX2(
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
# Don't support this chart in this game
|
# Don't support this chart in this game
|
||||||
continue
|
continue
|
||||||
gamerank = self.db_to_game_rank(score.data.get_int('rank'))
|
gamerank = self.db_to_game_rank(score.data.get_int("rank"))
|
||||||
combo_type = self.db_to_game_halo(score.data.get_int('halo'))
|
combo_type = self.db_to_game_halo(score.data.get_int("halo"))
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.u32("score", score.points))
|
||||||
typenode.add_child(Node.u16('count', score.plays))
|
typenode.add_child(Node.u16("count", score.plays))
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
typenode.add_child(Node.u8("rank", gamerank))
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
songid = int(request.attribute('mid'))
|
songid = int(request.attribute("mid"))
|
||||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
points = int(data.attribute('score'))
|
points = int(data.attribute("score"))
|
||||||
combo = int(data.attribute('combo'))
|
combo = int(data.attribute("combo"))
|
||||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||||
if int(data.attribute('full')) == 0:
|
if int(data.attribute("full")) == 0:
|
||||||
halo = self.HALO_NONE
|
halo = self.HALO_NONE
|
||||||
elif int(data.attribute('perf')) == 0:
|
elif int(data.attribute("perf")) == 0:
|
||||||
halo = self.HALO_GREAT_FULL_COMBO
|
halo = self.HALO_GREAT_FULL_COMBO
|
||||||
elif points < 1000000:
|
elif points < 1000000:
|
||||||
halo = self.HALO_PERFECT_FULL_COMBO
|
halo = self.HALO_PERFECT_FULL_COMBO
|
||||||
else:
|
else:
|
||||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
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
|
# 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
|
# an anonymous score, it only goes into the DB to count against the
|
||||||
@ -294,336 +305,372 @@ class DDRX2(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
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
|
# Look up play stats we bridge to every mix
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Basic game settings
|
# Basic game settings
|
||||||
root.add_child(Node.string('seq', ''))
|
root.add_child(Node.string("seq", ""))
|
||||||
root.add_child(Node.u32('code', profile.extid))
|
root.add_child(Node.u32("code", profile.extid))
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
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(
|
||||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||||
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_s", play_stats.get_int("single_plays")))
|
||||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
root.add_child(
|
||||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
) # This could be wrong, its a guess
|
||||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
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
|
# Character stuff
|
||||||
chara = Node.void('chara')
|
chara = Node.void("chara")
|
||||||
root.add_child(chara)
|
root.add_child(chara)
|
||||||
if 'chara' in profile:
|
if "chara" in profile:
|
||||||
chara.set_attribute('my', str(profile.get_int('chara')))
|
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
|
# Drill rankings
|
||||||
if 'title' in profile:
|
if "title" in profile:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
root.add_child(title)
|
root.add_child(title)
|
||||||
titledict = profile.get_dict('title')
|
titledict = profile.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in profile:
|
if "title_gr" in profile:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
root.add_child(title_gr)
|
root.add_child(title_gr)
|
||||||
title_grdict = profile.get_dict('title_gr')
|
title_grdict = profile.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Event progrses
|
# Event progrses
|
||||||
if 'event' in profile:
|
if "event" in profile:
|
||||||
event = Node.void('event')
|
event = Node.void("event")
|
||||||
root.add_child(event)
|
root.add_child(event)
|
||||||
event_dict = profile.get_dict('event')
|
event_dict = profile.get_dict("event")
|
||||||
if 'diff_sum' in event_dict:
|
if "diff_sum" in event_dict:
|
||||||
event.set_attribute('diff_sum', str(event_dict.get_int('diff_sum')))
|
event.set_attribute("diff_sum", str(event_dict.get_int("diff_sum")))
|
||||||
if 'welcome' in event_dict:
|
if "welcome" in event_dict:
|
||||||
event.set_attribute('welcome', str(event_dict.get_int('welcome')))
|
event.set_attribute("welcome", str(event_dict.get_int("welcome")))
|
||||||
if 'e_flags' in event_dict:
|
if "e_flags" in event_dict:
|
||||||
event.set_attribute('e_flags', str(event_dict.get_int('e_flags')))
|
event.set_attribute("e_flags", str(event_dict.get_int("e_flags")))
|
||||||
|
|
||||||
if 'e_panel' in profile:
|
if "e_panel" in profile:
|
||||||
e_panel = Node.void('e_panel')
|
e_panel = Node.void("e_panel")
|
||||||
root.add_child(e_panel)
|
root.add_child(e_panel)
|
||||||
e_panel_dict = profile.get_dict('e_panel')
|
e_panel_dict = profile.get_dict("e_panel")
|
||||||
if 'play_id' in e_panel_dict:
|
if "play_id" in e_panel_dict:
|
||||||
e_panel.set_attribute('play_id', str(e_panel_dict.get_int('play_id')))
|
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(
|
||||||
e_panel.add_child(Node.u8_array('panel_state', e_panel_dict.get_int_array('panel_state', 6)))
|
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:
|
if "e_pix" in profile:
|
||||||
e_pix = Node.void('e_pix')
|
e_pix = Node.void("e_pix")
|
||||||
root.add_child(e_pix)
|
root.add_child(e_pix)
|
||||||
e_pix_dict = profile.get_dict('e_pix')
|
e_pix_dict = profile.get_dict("e_pix")
|
||||||
if 'max_distance' in e_pix_dict:
|
if "max_distance" in e_pix_dict:
|
||||||
e_pix.set_attribute('max_distance', str(e_pix_dict.get_int('max_distance')))
|
e_pix.set_attribute(
|
||||||
if 'max_planet' in e_pix_dict:
|
"max_distance", str(e_pix_dict.get_int("max_distance"))
|
||||||
e_pix.set_attribute('max_planet', str(e_pix_dict.get_int('max_planet')))
|
)
|
||||||
if 'total_distance' in e_pix_dict:
|
if "max_planet" in e_pix_dict:
|
||||||
e_pix.set_attribute('total_distance', str(e_pix_dict.get_int('total_distance')))
|
e_pix.set_attribute("max_planet", str(e_pix_dict.get_int("max_planet")))
|
||||||
if 'total_planet' in e_pix_dict:
|
if "total_distance" in e_pix_dict:
|
||||||
e_pix.set_attribute('total_planet', str(e_pix_dict.get_int('total_planet')))
|
e_pix.set_attribute(
|
||||||
if 'border_character' in e_pix_dict:
|
"total_distance", str(e_pix_dict.get_int("total_distance"))
|
||||||
e_pix.set_attribute('border_character', str(e_pix_dict.get_int('border_character')))
|
)
|
||||||
if 'border_balloon' in e_pix_dict:
|
if "total_planet" in e_pix_dict:
|
||||||
e_pix.set_attribute('border_balloon', str(e_pix_dict.get_int('border_balloon')))
|
e_pix.set_attribute(
|
||||||
if 'border_music_aftr' in e_pix_dict:
|
"total_planet", str(e_pix_dict.get_int("total_planet"))
|
||||||
e_pix.set_attribute('border_music_aftr', str(e_pix_dict.get_int('border_music_aftr')))
|
)
|
||||||
if 'border_music_meii' in e_pix_dict:
|
if "border_character" in e_pix_dict:
|
||||||
e_pix.set_attribute('border_music_meii', str(e_pix_dict.get_int('border_music_meii')))
|
e_pix.set_attribute(
|
||||||
if 'border_music_dirt' in e_pix_dict:
|
"border_character", str(e_pix_dict.get_int("border_character"))
|
||||||
e_pix.set_attribute('border_music_dirt', str(e_pix_dict.get_int('border_music_dirt')))
|
)
|
||||||
if 'flags' in e_pix_dict:
|
if "border_balloon" in e_pix_dict:
|
||||||
e_pix.set_attribute('flags', str(e_pix_dict.get_int('flags')))
|
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
|
# Calorie mode
|
||||||
if 'weight' in profile:
|
if "weight" in profile:
|
||||||
workouts = self.data.local.user.get_time_based_achievements(
|
workouts = self.data.local.user.get_time_based_achievements(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
achievementtype='workout',
|
achievementtype="workout",
|
||||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
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])
|
||||||
workout = Node.void('workout')
|
workout = Node.void("workout")
|
||||||
root.add_child(workout)
|
root.add_child(workout)
|
||||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||||
workout.set_attribute('day', str(total))
|
workout.set_attribute("day", str(total))
|
||||||
workout.set_attribute('disp', '1')
|
workout.set_attribute("disp", "1")
|
||||||
|
|
||||||
# Last cursor settings
|
# Last cursor settings
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
root.add_child(last)
|
root.add_child(last)
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last.set_attribute('fri', str(lastdict.get_int('fri')))
|
last.set_attribute("fri", str(lastdict.get_int("fri")))
|
||||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
last.set_attribute("cid", str(lastdict.get_int("cid")))
|
||||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
last.set_attribute("ctype", str(lastdict.get_int("ctype")))
|
||||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
last.set_attribute("sid", str(lastdict.get_int("sid")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
root.add_child(gr_s)
|
root.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_s', 5):
|
for entry in profile.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
root.add_child(gr_d)
|
root.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_d', 5):
|
for entry in profile.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Options in menus
|
# Options in menus
|
||||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 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)))
|
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||||
|
|
||||||
# Unlock flags
|
# 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?
|
# 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
|
# Rivals
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
friend = self.get_profile(link.other_userid)
|
friend = self.get_profile(link.other_userid)
|
||||||
play_stats = self.get_play_statistics(link.other_userid)
|
play_stats = self.get_play_statistics(link.other_userid)
|
||||||
if friend is not None:
|
if friend is not None:
|
||||||
friendnode = Node.void('friend')
|
friendnode = Node.void("friend")
|
||||||
root.add_child(friendnode)
|
root.add_child(friendnode)
|
||||||
friendnode.set_attribute('pos', str(pos))
|
friendnode.set_attribute("pos", str(pos))
|
||||||
friendnode.set_attribute('vs', '0')
|
friendnode.set_attribute("vs", "0")
|
||||||
friendnode.set_attribute('up', '0')
|
friendnode.set_attribute("up", "0")
|
||||||
friendnode.add_child(Node.u32('code', friend.extid))
|
friendnode.add_child(Node.u32("code", friend.extid))
|
||||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
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(
|
||||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
)
|
||||||
|
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
|
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title' in friend:
|
if "title" in friend:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
friendnode.add_child(title)
|
friendnode.add_child(title)
|
||||||
titledict = friend.get_dict('title')
|
titledict = friend.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in friend:
|
if "title_gr" in friend:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
friendnode.add_child(title_gr)
|
friendnode.add_child(title_gr)
|
||||||
title_grdict = friend.get_dict('title_gr')
|
title_grdict = friend.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
friendnode.add_child(gr_s)
|
friendnode.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_s', 5):
|
for entry in friend.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
friendnode.add_child(gr_d)
|
friendnode.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_d', 5):
|
for entry in friend.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
return root
|
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()
|
newprofile = oldprofile.clone()
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Grab last node and accessories so we can make decisions based on type
|
# Grab last node and accessories so we can make decisions based on type
|
||||||
last = request.child('last')
|
last = request.child("last")
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
mode = int(last.attribute('mode'))
|
mode = int(last.attribute("mode"))
|
||||||
style = int(last.attribute('style'))
|
style = int(last.attribute("style"))
|
||||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
title = request.child('title')
|
title = request.child("title")
|
||||||
title_gr = request.child('title_gr')
|
title_gr = request.child("title_gr")
|
||||||
titledict = newprofile.get_dict('title')
|
titledict = newprofile.get_dict("title")
|
||||||
title_grdict = newprofile.get_dict('title_gr')
|
title_grdict = newprofile.get_dict("title_gr")
|
||||||
|
|
||||||
# Groove radar level ups
|
# 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
|
# Set the correct values depending on if we're single or double play
|
||||||
if is_dp:
|
if is_dp:
|
||||||
play_stats.increment_int('double_plays')
|
play_stats.increment_int("double_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_d',
|
"gr_d",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('d', title.value)
|
titledict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('d', title.value)
|
title_grdict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
else:
|
else:
|
||||||
play_stats.increment_int('single_plays')
|
play_stats.increment_int("single_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_s',
|
"gr_s",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('s', title.value)
|
titledict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('s', title.value)
|
title_grdict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
play_stats.increment_int(f'cnt_m{mode}')
|
play_stats.increment_int(f"cnt_m{mode}")
|
||||||
|
|
||||||
# Update last attributes
|
# Update last attributes
|
||||||
lastdict.replace_int('fri', intish(last.attribute('fri')))
|
lastdict.replace_int("fri", intish(last.attribute("fri")))
|
||||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Grab character options
|
# Grab character options
|
||||||
chara = request.child('chara')
|
chara = request.child("chara")
|
||||||
if chara is not None:
|
if chara is not None:
|
||||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||||
newprofile.replace_int_array('chara_opt', 96, request.child_value('chara_opt'))
|
newprofile.replace_int_array("chara_opt", 96, request.child_value("chara_opt"))
|
||||||
|
|
||||||
# Track event progress
|
# Track event progress
|
||||||
event = request.child('event')
|
event = request.child("event")
|
||||||
if event is not None:
|
if event is not None:
|
||||||
event_dict = newprofile.get_dict('event')
|
event_dict = newprofile.get_dict("event")
|
||||||
event_dict.replace_int('diff_sum', intish(event.attribute('diff_sum')))
|
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("e_flags", intish(event.attribute("e_flags")))
|
||||||
event_dict.replace_int('welcome', intish(event.attribute('welcome')))
|
event_dict.replace_int("welcome", intish(event.attribute("welcome")))
|
||||||
newprofile.replace_dict('event', event_dict)
|
newprofile.replace_dict("event", event_dict)
|
||||||
|
|
||||||
e_panel = request.child('e_panel')
|
e_panel = request.child("e_panel")
|
||||||
if e_panel is not None:
|
if e_panel is not None:
|
||||||
e_panel_dict = newprofile.get_dict('e_panel')
|
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("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("cell", 24, e_panel.child_value("cell"))
|
||||||
e_panel_dict.replace_int_array('panel_state', 6, e_panel.child_value('panel_state'))
|
e_panel_dict.replace_int_array(
|
||||||
newprofile.replace_dict('e_panel', e_panel_dict)
|
"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:
|
if e_pix is not None:
|
||||||
e_pix_dict = newprofile.get_dict('e_pix')
|
e_pix_dict = newprofile.get_dict("e_pix")
|
||||||
max_distance = e_pix_dict.get_int('max_distance')
|
max_distance = e_pix_dict.get_int("max_distance")
|
||||||
max_planet = e_pix_dict.get_int('max_planet')
|
max_planet = e_pix_dict.get_int("max_planet")
|
||||||
total_distance = e_pix_dict.get_int('total_distance')
|
total_distance = e_pix_dict.get_int("total_distance")
|
||||||
total_planet = e_pix_dict.get_int('total_planet')
|
total_planet = e_pix_dict.get_int("total_planet")
|
||||||
cur_distance = intish(e_pix.attribute('this_distance'))
|
cur_distance = intish(e_pix.attribute("this_distance"))
|
||||||
cur_planet = intish(e_pix.attribute('this_planet'))
|
cur_planet = intish(e_pix.attribute("this_planet"))
|
||||||
if cur_distance is not None:
|
if cur_distance is not None:
|
||||||
max_distance = max(max_distance, cur_distance)
|
max_distance = max(max_distance, cur_distance)
|
||||||
total_distance += cur_distance
|
total_distance += cur_distance
|
||||||
@ -631,51 +678,51 @@ class DDRX2(
|
|||||||
max_planet = max(max_planet, cur_planet)
|
max_planet = max(max_planet, cur_planet)
|
||||||
total_planet += cur_planet
|
total_planet += cur_planet
|
||||||
|
|
||||||
e_pix_dict.replace_int('max_distance', max_distance)
|
e_pix_dict.replace_int("max_distance", max_distance)
|
||||||
e_pix_dict.replace_int('max_planet', max_planet)
|
e_pix_dict.replace_int("max_planet", max_planet)
|
||||||
e_pix_dict.replace_int('total_distance', total_distance)
|
e_pix_dict.replace_int("total_distance", total_distance)
|
||||||
e_pix_dict.replace_int('total_planet', total_planet)
|
e_pix_dict.replace_int("total_planet", total_planet)
|
||||||
e_pix_dict.replace_int('flags', intish(e_pix.attribute('flags')))
|
e_pix_dict.replace_int("flags", intish(e_pix.attribute("flags")))
|
||||||
newprofile.replace_dict('e_pix', e_pix_dict)
|
newprofile.replace_dict("e_pix", e_pix_dict)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
opt = request.child('opt')
|
opt = request.child("opt")
|
||||||
if opt is not None:
|
if opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Experience and stars
|
||||||
exp = request.child_value('exp')
|
exp = request.child_value("exp")
|
||||||
if exp is not None:
|
if exp is not None:
|
||||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||||
star = request.child_value('star')
|
star = request.child_value("star")
|
||||||
if star is not None:
|
if star is not None:
|
||||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||||
star_c = request.child_value('star_c')
|
star_c = request.child_value("star_c")
|
||||||
if star_c is not None:
|
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
|
# Update game flags
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'flag':
|
if child.name != "flag":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
value = int(child.attribute('data'))
|
value = int(child.attribute("data"))
|
||||||
offset = int(child.attribute('no'))
|
offset = int(child.attribute("no"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
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):
|
if offset < 0 or offset >= len(flags):
|
||||||
continue
|
continue
|
||||||
flags[offset] = value
|
flags[offset] = value
|
||||||
newprofile.replace_int_array('flag', 256, flags)
|
newprofile.replace_int_array("flag", 256, flags)
|
||||||
|
|
||||||
# Workout mode support
|
# Workout mode support
|
||||||
newweight = -1
|
newweight = -1
|
||||||
oldweight = newprofile.get_int('weight')
|
oldweight = newprofile.get_int("weight")
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'weight':
|
if child.name != "weight":
|
||||||
continue
|
continue
|
||||||
newweight = child.value
|
newweight = child.value
|
||||||
if newweight < 0:
|
if newweight < 0:
|
||||||
@ -684,14 +731,14 @@ class DDRX2(
|
|||||||
# Either update or unset the weight depending on the game
|
# Either update or unset the weight depending on the game
|
||||||
if newweight == 0:
|
if newweight == 0:
|
||||||
# Weight is unset or we declined to use this feature, remove from profile
|
# Weight is unset or we declined to use this feature, remove from profile
|
||||||
if 'weight' in newprofile:
|
if "weight" in newprofile:
|
||||||
del newprofile['weight']
|
del newprofile["weight"]
|
||||||
else:
|
else:
|
||||||
# Weight has been set or previously retrieved, we should save calories
|
# Weight has been set or previously retrieved, we should save calories
|
||||||
newprofile.replace_int('weight', newweight)
|
newprofile.replace_int("weight", newweight)
|
||||||
total = 0
|
total = 0
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'calory':
|
if child.name != "calory":
|
||||||
continue
|
continue
|
||||||
total += child.value
|
total += child.value
|
||||||
self.data.local.user.put_time_based_achievement(
|
self.data.local.user.put_time_based_achievement(
|
||||||
@ -699,10 +746,10 @@ class DDRX2(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
0,
|
0,
|
||||||
'workout',
|
"workout",
|
||||||
{
|
{
|
||||||
'calories': total,
|
"calories": total,
|
||||||
'weight': newweight,
|
"weight": newweight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -710,7 +757,7 @@ class DDRX2(
|
|||||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
@ -719,11 +766,11 @@ class DDRX2(
|
|||||||
# Save any rivals that were added/removed/changed
|
# Save any rivals that were added/removed/changed
|
||||||
newfriends = oldfriends[:]
|
newfriends = oldfriends[:]
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'friend':
|
if child.name != "friend":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
code = int(child.attribute('code'))
|
code = int(child.attribute("code"))
|
||||||
pos = int(child.attribute('pos'))
|
pos = int(child.attribute("pos"))
|
||||||
|
|
||||||
if pos >= 0 and pos < 10:
|
if pos >= 0 and pos < 10:
|
||||||
if code == 0:
|
if code == 0:
|
||||||
@ -731,7 +778,9 @@ class DDRX2(
|
|||||||
newfriends[pos] = None
|
newfriends[pos] = None
|
||||||
else:
|
else:
|
||||||
# Try looking up the userid
|
# 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
|
# Diff the set of links to determine updates
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
@ -744,7 +793,7 @@ class DDRX2(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
elif oldfriends[i] is None:
|
elif oldfriends[i] is None:
|
||||||
@ -753,7 +802,7 @@ class DDRX2(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
@ -763,14 +812,14 @@ class DDRX2(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
self.data.local.user.put_link(
|
self.data.local.user.put_link(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
@ -49,7 +49,7 @@ class DDRX3(
|
|||||||
DDRBase,
|
DDRBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution X3 VS 2ndMIX'
|
name: str = "DanceDanceRevolution X3 VS 2ndMIX"
|
||||||
version: int = VersionConstants.DDR_X3_VS_2NDMIX
|
version: int = VersionConstants.DDR_X3_VS_2NDMIX
|
||||||
|
|
||||||
GAME_STYLE_SINGLE: Final[int] = 0
|
GAME_STYLE_SINGLE: Final[int] = 0
|
||||||
@ -156,28 +156,30 @@ class DDRX3(
|
|||||||
return combo_type
|
return combo_type
|
||||||
|
|
||||||
def handle_game_common_request(self, request: Node) -> Node:
|
def handle_game_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for flagid in range(256):
|
for flagid in range(256):
|
||||||
flag = Node.void('flag')
|
flag = Node.void("flag")
|
||||||
game.add_child(flag)
|
game.add_child(flag)
|
||||||
|
|
||||||
flag.set_attribute('id', str(flagid))
|
flag.set_attribute("id", str(flagid))
|
||||||
flag.set_attribute('s2', '0')
|
flag.set_attribute("s2", "0")
|
||||||
flag.set_attribute('s1', '0')
|
flag.set_attribute("s1", "0")
|
||||||
flag.set_attribute('t', '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
|
counts_by_reflink = [0] * self.GAME_MAX_SONGS
|
||||||
for (reflink, plays) in hit_chart:
|
for (reflink, plays) in hit_chart:
|
||||||
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
if reflink >= 0 and reflink < self.GAME_MAX_SONGS:
|
||||||
counts_by_reflink[reflink] = plays
|
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
|
return game
|
||||||
|
|
||||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||||
extid = intish(request.attribute('code'))
|
extid = intish(request.attribute("code"))
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if extid is not None:
|
if extid is not None:
|
||||||
# Rival score loading
|
# Rival score loading
|
||||||
@ -187,10 +189,15 @@ class DDRX3(
|
|||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
|
|
||||||
if userid is not 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
|
||||||
|
)
|
||||||
old_scores = [
|
old_scores = [
|
||||||
score for score in self.data.local.user.get_achievements(self.game, self.music_version, userid)
|
score
|
||||||
if score.type == '2ndmix'
|
for score in self.data.local.user.get_achievements(
|
||||||
|
self.game, self.music_version, userid
|
||||||
|
)
|
||||||
|
if score.type == "2ndmix"
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
@ -203,7 +210,7 @@ class DDRX3(
|
|||||||
sortedscores[score.id] = {}
|
sortedscores[score.id] = {}
|
||||||
if score.chart not in sortedscores[score.id]:
|
if score.chart not in sortedscores[score.id]:
|
||||||
sortedscores[score.id][score.chart] = {}
|
sortedscores[score.id][score.chart] = {}
|
||||||
sortedscores[score.id][score.chart]['score'] = score
|
sortedscores[score.id][score.chart]["score"] = score
|
||||||
|
|
||||||
for oldscore in old_scores:
|
for oldscore in old_scores:
|
||||||
songid = int(oldscore.id / 100)
|
songid = int(oldscore.id / 100)
|
||||||
@ -212,13 +219,13 @@ class DDRX3(
|
|||||||
sortedscores[songid] = {}
|
sortedscores[songid] = {}
|
||||||
if chart not in sortedscores[songid]:
|
if chart not in sortedscores[songid]:
|
||||||
sortedscores[songid][chart] = {}
|
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:
|
for song in sortedscores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(music)
|
game.add_child(music)
|
||||||
music.set_attribute('reclink', str(song))
|
music.set_attribute("reclink", str(song))
|
||||||
|
|
||||||
for chart in sortedscores[song]:
|
for chart in sortedscores[song]:
|
||||||
try:
|
try:
|
||||||
@ -228,12 +235,16 @@ class DDRX3(
|
|||||||
continue
|
continue
|
||||||
scoredict = sortedscores[song][chart]
|
scoredict = sortedscores[song][chart]
|
||||||
|
|
||||||
if 'score' in scoredict:
|
if "score" in scoredict:
|
||||||
# We played the normal version of this song
|
# We played the normal version of this song
|
||||||
gamerank = self.db_to_game_rank(scoredict['score'].data.get_int('rank'))
|
gamerank = self.db_to_game_rank(
|
||||||
combo_type = self.db_to_game_halo(scoredict['score'].data.get_int('halo'))
|
scoredict["score"].data.get_int("rank")
|
||||||
points = scoredict['score'].points # type: ignore
|
)
|
||||||
plays = scoredict['score'].plays # type: ignore
|
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:
|
else:
|
||||||
# We only played 2nd mix version of this song
|
# We only played 2nd mix version of this song
|
||||||
gamerank = 0
|
gamerank = 0
|
||||||
@ -241,45 +252,45 @@ class DDRX3(
|
|||||||
points = 0
|
points = 0
|
||||||
plays = 0
|
plays = 0
|
||||||
|
|
||||||
if 'oldscore' in scoredict:
|
if "oldscore" in scoredict:
|
||||||
# We played the 2nd mix version of this song
|
# We played the 2nd mix version of this song
|
||||||
oldpoints = scoredict['oldscore'].data.get_int('points')
|
oldpoints = scoredict["oldscore"].data.get_int("points")
|
||||||
oldrank = scoredict['oldscore'].data.get_int('rank')
|
oldrank = scoredict["oldscore"].data.get_int("rank")
|
||||||
oldplays = scoredict['oldscore'].data.get_int('plays')
|
oldplays = scoredict["oldscore"].data.get_int("plays")
|
||||||
else:
|
else:
|
||||||
oldpoints = 0
|
oldpoints = 0
|
||||||
oldrank = 0
|
oldrank = 0
|
||||||
oldplays = 0
|
oldplays = 0
|
||||||
|
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
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.u32("score", points))
|
||||||
typenode.add_child(Node.u16('count', plays))
|
typenode.add_child(Node.u16("count", plays))
|
||||||
typenode.add_child(Node.u8('rank', gamerank))
|
typenode.add_child(Node.u8("rank", gamerank))
|
||||||
typenode.add_child(Node.u8('combo_type', combo_type))
|
typenode.add_child(Node.u8("combo_type", combo_type))
|
||||||
typenode.add_child(Node.u32('score_2nd', oldpoints))
|
typenode.add_child(Node.u32("score_2nd", oldpoints))
|
||||||
typenode.add_child(Node.u8('rank_2nd', oldrank))
|
typenode.add_child(Node.u8("rank_2nd", oldrank))
|
||||||
typenode.add_child(Node.u16('cnt_2nd', oldplays))
|
typenode.add_child(Node.u16("cnt_2nd", oldplays))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
songid = int(request.attribute('mid'))
|
songid = int(request.attribute("mid"))
|
||||||
chart = self.game_to_db_chart(int(request.attribute('mtype')))
|
chart = self.game_to_db_chart(int(request.attribute("mtype")))
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
|
|
||||||
# Calculate statistics
|
# Calculate statistics
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
playmode = int(data.attribute('playmode'))
|
playmode = int(data.attribute("playmode"))
|
||||||
if playmode == self.GAME_PLAY_MODE_2NDMIX:
|
if playmode == self.GAME_PLAY_MODE_2NDMIX:
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
# These are special cased and treated like achievements
|
# These are special cased and treated like achievements
|
||||||
points = int(data.attribute('score_2nd'))
|
points = int(data.attribute("score_2nd"))
|
||||||
combo = int(data.attribute('combo_2nd'))
|
combo = int(data.attribute("combo_2nd"))
|
||||||
rank = int(data.attribute('rank_2nd'))
|
rank = int(data.attribute("rank_2nd"))
|
||||||
|
|
||||||
# Grab the old 2nd mix score
|
# Grab the old 2nd mix score
|
||||||
existingscore = self.data.local.user.get_achievement(
|
existingscore = self.data.local.user.get_achievement(
|
||||||
@ -287,17 +298,17 @@ class DDRX3(
|
|||||||
self.music_version,
|
self.music_version,
|
||||||
userid,
|
userid,
|
||||||
(songid * 100) + chart,
|
(songid * 100) + chart,
|
||||||
'2ndmix',
|
"2ndmix",
|
||||||
)
|
)
|
||||||
|
|
||||||
if existingscore is not None:
|
if existingscore is not None:
|
||||||
highscore = points > existingscore.get_int('points')
|
highscore = points > existingscore.get_int("points")
|
||||||
|
|
||||||
plays = existingscore.get_int('plays', 0) + 1
|
plays = existingscore.get_int("plays", 0) + 1
|
||||||
points = max(points, existingscore.get_int('points'))
|
points = max(points, existingscore.get_int("points"))
|
||||||
if not highscore:
|
if not highscore:
|
||||||
combo = existingscore.get_int('combo', combo)
|
combo = existingscore.get_int("combo", combo)
|
||||||
rank = existingscore.get_int('rank', rank)
|
rank = existingscore.get_int("rank", rank)
|
||||||
else:
|
else:
|
||||||
plays = 1
|
plays = 1
|
||||||
|
|
||||||
@ -306,27 +317,27 @@ class DDRX3(
|
|||||||
self.music_version,
|
self.music_version,
|
||||||
userid,
|
userid,
|
||||||
(songid * 100) + chart,
|
(songid * 100) + chart,
|
||||||
'2ndmix',
|
"2ndmix",
|
||||||
{
|
{
|
||||||
'points': points,
|
"points": points,
|
||||||
'combo': combo,
|
"combo": combo,
|
||||||
'rank': rank,
|
"rank": rank,
|
||||||
'plays': plays,
|
"plays": plays,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
points = int(data.attribute('score'))
|
points = int(data.attribute("score"))
|
||||||
combo = int(data.attribute('combo'))
|
combo = int(data.attribute("combo"))
|
||||||
rank = self.game_to_db_rank(int(data.attribute('rank')))
|
rank = self.game_to_db_rank(int(data.attribute("rank")))
|
||||||
if int(data.attribute('full')) == 0:
|
if int(data.attribute("full")) == 0:
|
||||||
halo = self.HALO_NONE
|
halo = self.HALO_NONE
|
||||||
elif int(data.attribute('perf')) == 0:
|
elif int(data.attribute("perf")) == 0:
|
||||||
halo = self.HALO_GREAT_FULL_COMBO
|
halo = self.HALO_GREAT_FULL_COMBO
|
||||||
elif points < 1000000:
|
elif points < 1000000:
|
||||||
halo = self.HALO_PERFECT_FULL_COMBO
|
halo = self.HALO_PERFECT_FULL_COMBO
|
||||||
else:
|
else:
|
||||||
halo = self.HALO_MARVELOUS_FULL_COMBO
|
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
|
# 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
|
# an anonymous score, it only goes into the DB to count against the
|
||||||
@ -343,355 +354,371 @@ class DDRX3(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# No response needed
|
# No response needed
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
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
|
# Look up play stats we bridge to every mix
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Basic game settings
|
# Basic game settings
|
||||||
root.add_child(Node.string('seq', ''))
|
root.add_child(Node.string("seq", ""))
|
||||||
root.add_child(Node.u32('code', profile.extid))
|
root.add_child(Node.u32("code", profile.extid))
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
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(
|
||||||
root.add_child(Node.u32('cnt_s', play_stats.get_int('single_plays')))
|
Node.u8("area", profile.get_int("area", self.get_machine_region()))
|
||||||
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_s", play_stats.get_int("single_plays")))
|
||||||
root.add_child(Node.u32('cnt_m0', play_stats.get_int('cnt_m0')))
|
root.add_child(Node.u32("cnt_d", play_stats.get_int("double_plays")))
|
||||||
root.add_child(Node.u32('cnt_m1', play_stats.get_int('cnt_m1')))
|
root.add_child(
|
||||||
root.add_child(Node.u32('cnt_m2', play_stats.get_int('cnt_m2')))
|
Node.u32("cnt_b", play_stats.get_int("battle_plays"))
|
||||||
root.add_child(Node.u32('cnt_m3', play_stats.get_int('cnt_m3')))
|
) # This could be wrong, its a guess
|
||||||
root.add_child(Node.u32('cnt_m4', play_stats.get_int('cnt_m4')))
|
root.add_child(Node.u32("cnt_m0", play_stats.get_int("cnt_m0")))
|
||||||
root.add_child(Node.u32('cnt_m5', play_stats.get_int('cnt_m5')))
|
root.add_child(Node.u32("cnt_m1", play_stats.get_int("cnt_m1")))
|
||||||
root.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
root.add_child(Node.u32("cnt_m2", play_stats.get_int("cnt_m2")))
|
||||||
root.add_child(Node.u32('exp_o', profile.get_int('exp_o')))
|
root.add_child(Node.u32("cnt_m3", play_stats.get_int("cnt_m3")))
|
||||||
root.add_child(Node.u32('star', profile.get_int('star')))
|
root.add_child(Node.u32("cnt_m4", play_stats.get_int("cnt_m4")))
|
||||||
root.add_child(Node.u32('star_c', profile.get_int('star_c')))
|
root.add_child(Node.u32("cnt_m5", play_stats.get_int("cnt_m5")))
|
||||||
root.add_child(Node.u8('combo', profile.get_int('combo', 0)))
|
root.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
root.add_child(Node.u8('timing_diff', profile.get_int('early_late', 0)))
|
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
|
# Character stuff
|
||||||
chara = Node.void('chara')
|
chara = Node.void("chara")
|
||||||
root.add_child(chara)
|
root.add_child(chara)
|
||||||
chara.set_attribute('my', str(profile.get_int('chara', 30)))
|
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)))
|
root.add_child(
|
||||||
|
Node.u16_array(
|
||||||
|
"chara_opt", profile.get_int_array("chara_opt", 96, [208] * 96)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title_gr' in profile:
|
if "title_gr" in profile:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
root.add_child(title_gr)
|
root.add_child(title_gr)
|
||||||
title_grdict = profile.get_dict('title_gr')
|
title_grdict = profile.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Calorie mode
|
# Calorie mode
|
||||||
if 'weight' in profile:
|
if "weight" in profile:
|
||||||
workouts = self.data.local.user.get_time_based_achievements(
|
workouts = self.data.local.user.get_time_based_achievements(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
achievementtype='workout',
|
achievementtype="workout",
|
||||||
since=Time.now() - Time.SECONDS_IN_DAY,
|
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])
|
||||||
workout = Node.void('workout')
|
workout = Node.void("workout")
|
||||||
root.add_child(workout)
|
root.add_child(workout)
|
||||||
workout.set_attribute('weight', str(profile.get_int('weight')))
|
workout.set_attribute("weight", str(profile.get_int("weight")))
|
||||||
workout.set_attribute('day', str(total))
|
workout.set_attribute("day", str(total))
|
||||||
workout.set_attribute('disp', '1')
|
workout.set_attribute("disp", "1")
|
||||||
|
|
||||||
# Daily play counts
|
# Daily play counts
|
||||||
daycount = Node.void('daycount')
|
daycount = Node.void("daycount")
|
||||||
root.add_child(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
|
# Daily combo stuff, unknown how this works
|
||||||
dailycombo = Node.void('dailycombo')
|
dailycombo = Node.void("dailycombo")
|
||||||
root.add_child(dailycombo)
|
root.add_child(dailycombo)
|
||||||
dailycombo.set_attribute('daily_combo', str(0))
|
dailycombo.set_attribute("daily_combo", str(0))
|
||||||
dailycombo.set_attribute('daily_combo_lv', str(0))
|
dailycombo.set_attribute("daily_combo_lv", str(0))
|
||||||
|
|
||||||
# Last cursor settings
|
# Last cursor settings
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
root.add_child(last)
|
root.add_child(last)
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last.set_attribute('rival1', str(lastdict.get_int('rival1', -1)))
|
last.set_attribute("rival1", str(lastdict.get_int("rival1", -1)))
|
||||||
last.set_attribute('rival2', str(lastdict.get_int('rival2', -1)))
|
last.set_attribute("rival2", str(lastdict.get_int("rival2", -1)))
|
||||||
last.set_attribute('rival3', str(lastdict.get_int('rival3', -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(
|
||||||
last.set_attribute('style', str(lastdict.get_int('style')))
|
"fri", str(lastdict.get_int("rival1", -1))
|
||||||
last.set_attribute('mode', str(lastdict.get_int('mode')))
|
) # This literally goes to the same memory in X3
|
||||||
last.set_attribute('cate', str(lastdict.get_int('cate')))
|
last.set_attribute("style", str(lastdict.get_int("style")))
|
||||||
last.set_attribute('sort', str(lastdict.get_int('sort')))
|
last.set_attribute("mode", str(lastdict.get_int("mode")))
|
||||||
last.set_attribute('mid', str(lastdict.get_int('mid')))
|
last.set_attribute("cate", str(lastdict.get_int("cate")))
|
||||||
last.set_attribute('mtype', str(lastdict.get_int('mtype')))
|
last.set_attribute("sort", str(lastdict.get_int("sort")))
|
||||||
last.set_attribute('cid', str(lastdict.get_int('cid')))
|
last.set_attribute("mid", str(lastdict.get_int("mid")))
|
||||||
last.set_attribute('ctype', str(lastdict.get_int('ctype')))
|
last.set_attribute("mtype", str(lastdict.get_int("mtype")))
|
||||||
last.set_attribute('sid', str(lastdict.get_int('sid')))
|
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 stars
|
||||||
result_star = Node.void('result_star')
|
result_star = Node.void("result_star")
|
||||||
root.add_child(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):
|
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 stuff
|
||||||
target = Node.void('target')
|
target = Node.void("target")
|
||||||
root.add_child(target)
|
root.add_child(target)
|
||||||
target.set_attribute('flag', str(profile.get_int('target_flag')))
|
target.set_attribute("flag", str(profile.get_int("target_flag")))
|
||||||
target.set_attribute('setnum', str(profile.get_int('target_setnum')))
|
target.set_attribute("setnum", str(profile.get_int("target_setnum")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
root.add_child(gr_s)
|
root.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_s', 5):
|
for entry in profile.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
root.add_child(gr_d)
|
root.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in profile.get_int_array('gr_d', 5):
|
for entry in profile.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Options in menus
|
# Options in menus
|
||||||
root.add_child(Node.s16_array('opt', profile.get_int_array('opt', 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)))
|
root.add_child(Node.s16_array("opt_ex", profile.get_int_array("opt_ex", 16)))
|
||||||
|
|
||||||
# Unlock flags
|
# 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?
|
# 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
|
# Rivals
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
friend = self.get_profile(link.other_userid)
|
friend = self.get_profile(link.other_userid)
|
||||||
play_stats = self.get_play_statistics(link.other_userid)
|
play_stats = self.get_play_statistics(link.other_userid)
|
||||||
if friend is not None:
|
if friend is not None:
|
||||||
friendnode = Node.void('friend')
|
friendnode = Node.void("friend")
|
||||||
root.add_child(friendnode)
|
root.add_child(friendnode)
|
||||||
friendnode.set_attribute('pos', str(pos))
|
friendnode.set_attribute("pos", str(pos))
|
||||||
friendnode.set_attribute('vs', '0')
|
friendnode.set_attribute("vs", "0")
|
||||||
friendnode.set_attribute('up', '0')
|
friendnode.set_attribute("up", "0")
|
||||||
friendnode.add_child(Node.u32('code', friend.extid))
|
friendnode.add_child(Node.u32("code", friend.extid))
|
||||||
friendnode.add_child(Node.string('name', friend.get_str('name')))
|
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(
|
||||||
friendnode.add_child(Node.u32('exp', play_stats.get_int('exp')))
|
Node.u8("area", friend.get_int("area", self.get_machine_region()))
|
||||||
friendnode.add_child(Node.u32('star', friend.get_int('star')))
|
)
|
||||||
|
friendnode.add_child(Node.u32("exp", play_stats.get_int("exp")))
|
||||||
|
friendnode.add_child(Node.u32("star", friend.get_int("star")))
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
if 'title' in friend:
|
if "title" in friend:
|
||||||
title = Node.void('title')
|
title = Node.void("title")
|
||||||
friendnode.add_child(title)
|
friendnode.add_child(title)
|
||||||
titledict = friend.get_dict('title')
|
titledict = friend.get_dict("title")
|
||||||
if 't' in titledict:
|
if "t" in titledict:
|
||||||
title.set_attribute('t', str(titledict.get_int('t')))
|
title.set_attribute("t", str(titledict.get_int("t")))
|
||||||
if 's' in titledict:
|
if "s" in titledict:
|
||||||
title.set_attribute('s', str(titledict.get_int('s')))
|
title.set_attribute("s", str(titledict.get_int("s")))
|
||||||
if 'd' in titledict:
|
if "d" in titledict:
|
||||||
title.set_attribute('d', str(titledict.get_int('d')))
|
title.set_attribute("d", str(titledict.get_int("d")))
|
||||||
|
|
||||||
if 'title_gr' in friend:
|
if "title_gr" in friend:
|
||||||
title_gr = Node.void('title_gr')
|
title_gr = Node.void("title_gr")
|
||||||
friendnode.add_child(title_gr)
|
friendnode.add_child(title_gr)
|
||||||
title_grdict = friend.get_dict('title_gr')
|
title_grdict = friend.get_dict("title_gr")
|
||||||
if 't' in title_grdict:
|
if "t" in title_grdict:
|
||||||
title_gr.set_attribute('t', str(title_grdict.get_int('t')))
|
title_gr.set_attribute("t", str(title_grdict.get_int("t")))
|
||||||
if 's' in title_grdict:
|
if "s" in title_grdict:
|
||||||
title_gr.set_attribute('s', str(title_grdict.get_int('s')))
|
title_gr.set_attribute("s", str(title_grdict.get_int("s")))
|
||||||
if 'd' in title_grdict:
|
if "d" in title_grdict:
|
||||||
title_gr.set_attribute('d', str(title_grdict.get_int('d')))
|
title_gr.set_attribute("d", str(title_grdict.get_int("d")))
|
||||||
|
|
||||||
# Groove gauge level-ups
|
# Groove gauge level-ups
|
||||||
gr_s = Node.void('gr_s')
|
gr_s = Node.void("gr_s")
|
||||||
friendnode.add_child(gr_s)
|
friendnode.add_child(gr_s)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_s', 5):
|
for entry in friend.get_int_array("gr_s", 5):
|
||||||
gr_s.set_attribute(f'gr{index}', str(entry))
|
gr_s.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
gr_d = Node.void('gr_d')
|
gr_d = Node.void("gr_d")
|
||||||
friendnode.add_child(gr_d)
|
friendnode.add_child(gr_d)
|
||||||
index = 1
|
index = 1
|
||||||
for entry in friend.get_int_array('gr_d', 5):
|
for entry in friend.get_int_array("gr_d", 5):
|
||||||
gr_d.set_attribute(f'gr{index}', str(entry))
|
gr_d.set_attribute(f"gr{index}", str(entry))
|
||||||
index = index + 1
|
index = index + 1
|
||||||
|
|
||||||
# Play area
|
# Play area
|
||||||
areas = profile.get_int_array('play_area', 55)
|
areas = profile.get_int_array("play_area", 55)
|
||||||
play_area = Node.void('play_area')
|
play_area = Node.void("play_area")
|
||||||
root.add_child(play_area)
|
root.add_child(play_area)
|
||||||
for i in range(len(areas)):
|
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
|
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()
|
newprofile = oldprofile.clone()
|
||||||
play_stats = self.get_play_statistics(userid)
|
play_stats = self.get_play_statistics(userid)
|
||||||
|
|
||||||
# Grab last node and accessories so we can make decisions based on type
|
# Grab last node and accessories so we can make decisions based on type
|
||||||
last = request.child('last')
|
last = request.child("last")
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
mode = int(last.attribute('mode'))
|
mode = int(last.attribute("mode"))
|
||||||
style = int(last.attribute('style'))
|
style = int(last.attribute("style"))
|
||||||
is_dp = style == self.GAME_STYLE_DOUBLE
|
is_dp = style == self.GAME_STYLE_DOUBLE
|
||||||
|
|
||||||
# Drill rankings
|
# Drill rankings
|
||||||
title = request.child('title')
|
title = request.child("title")
|
||||||
title_gr = request.child('title_gr')
|
title_gr = request.child("title_gr")
|
||||||
titledict = newprofile.get_dict('title')
|
titledict = newprofile.get_dict("title")
|
||||||
title_grdict = newprofile.get_dict('title_gr')
|
title_grdict = newprofile.get_dict("title_gr")
|
||||||
|
|
||||||
# Groove radar level ups
|
# 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
|
# Set the correct values depending on if we're single or double play
|
||||||
if is_dp:
|
if is_dp:
|
||||||
play_stats.increment_int('double_plays')
|
play_stats.increment_int("double_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_d',
|
"gr_d",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('d', title.value)
|
titledict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('d', title.value)
|
title_grdict.replace_int("d", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
else:
|
else:
|
||||||
play_stats.increment_int('single_plays')
|
play_stats.increment_int("single_plays")
|
||||||
if gr is not None:
|
if gr is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'gr_s',
|
"gr_s",
|
||||||
5,
|
5,
|
||||||
[
|
[
|
||||||
intish(gr.attribute('gr1')),
|
intish(gr.attribute("gr1")),
|
||||||
intish(gr.attribute('gr2')),
|
intish(gr.attribute("gr2")),
|
||||||
intish(gr.attribute('gr3')),
|
intish(gr.attribute("gr3")),
|
||||||
intish(gr.attribute('gr4')),
|
intish(gr.attribute("gr4")),
|
||||||
intish(gr.attribute('gr5')),
|
intish(gr.attribute("gr5")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
if title is not None:
|
if title is not None:
|
||||||
titledict.replace_int('s', title.value)
|
titledict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title', titledict)
|
newprofile.replace_dict("title", titledict)
|
||||||
if title_gr is not None:
|
if title_gr is not None:
|
||||||
title_grdict.replace_int('s', title.value)
|
title_grdict.replace_int("s", title.value)
|
||||||
newprofile.replace_dict('title_gr', title_grdict)
|
newprofile.replace_dict("title_gr", title_grdict)
|
||||||
play_stats.increment_int(f'cnt_m{mode}')
|
play_stats.increment_int(f"cnt_m{mode}")
|
||||||
|
|
||||||
# Result stars
|
# Result stars
|
||||||
result_star = request.child('result_star')
|
result_star = request.child("result_star")
|
||||||
if result_star is not None:
|
if result_star is not None:
|
||||||
newprofile.replace_int_array(
|
newprofile.replace_int_array(
|
||||||
'result_stars',
|
"result_stars",
|
||||||
9,
|
9,
|
||||||
[
|
[
|
||||||
intish(result_star.attribute('slot1')),
|
intish(result_star.attribute("slot1")),
|
||||||
intish(result_star.attribute('slot2')),
|
intish(result_star.attribute("slot2")),
|
||||||
intish(result_star.attribute('slot3')),
|
intish(result_star.attribute("slot3")),
|
||||||
intish(result_star.attribute('slot4')),
|
intish(result_star.attribute("slot4")),
|
||||||
intish(result_star.attribute('slot5')),
|
intish(result_star.attribute("slot5")),
|
||||||
intish(result_star.attribute('slot6')),
|
intish(result_star.attribute("slot6")),
|
||||||
intish(result_star.attribute('slot7')),
|
intish(result_star.attribute("slot7")),
|
||||||
intish(result_star.attribute('slot8')),
|
intish(result_star.attribute("slot8")),
|
||||||
intish(result_star.attribute('slot9')),
|
intish(result_star.attribute("slot9")),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Target stuff
|
# Target stuff
|
||||||
target = request.child('target')
|
target = request.child("target")
|
||||||
if target is not None:
|
if target is not None:
|
||||||
newprofile.replace_int('target_flag', intish(target.attribute('flag')))
|
newprofile.replace_int("target_flag", intish(target.attribute("flag")))
|
||||||
newprofile.replace_int('target_setnum', intish(target.attribute('setnum')))
|
newprofile.replace_int("target_setnum", intish(target.attribute("setnum")))
|
||||||
|
|
||||||
# Update last attributes
|
# Update last attributes
|
||||||
lastdict.replace_int('rival1', intish(last.attribute('rival1')))
|
lastdict.replace_int("rival1", intish(last.attribute("rival1")))
|
||||||
lastdict.replace_int('rival2', intish(last.attribute('rival2')))
|
lastdict.replace_int("rival2", intish(last.attribute("rival2")))
|
||||||
lastdict.replace_int('rival3', intish(last.attribute('rival3')))
|
lastdict.replace_int("rival3", intish(last.attribute("rival3")))
|
||||||
lastdict.replace_int('style', intish(last.attribute('style')))
|
lastdict.replace_int("style", intish(last.attribute("style")))
|
||||||
lastdict.replace_int('mode', intish(last.attribute('mode')))
|
lastdict.replace_int("mode", intish(last.attribute("mode")))
|
||||||
lastdict.replace_int('cate', intish(last.attribute('cate')))
|
lastdict.replace_int("cate", intish(last.attribute("cate")))
|
||||||
lastdict.replace_int('sort', intish(last.attribute('sort')))
|
lastdict.replace_int("sort", intish(last.attribute("sort")))
|
||||||
lastdict.replace_int('mid', intish(last.attribute('mid')))
|
lastdict.replace_int("mid", intish(last.attribute("mid")))
|
||||||
lastdict.replace_int('mtype', intish(last.attribute('mtype')))
|
lastdict.replace_int("mtype", intish(last.attribute("mtype")))
|
||||||
lastdict.replace_int('cid', intish(last.attribute('cid')))
|
lastdict.replace_int("cid", intish(last.attribute("cid")))
|
||||||
lastdict.replace_int('ctype', intish(last.attribute('ctype')))
|
lastdict.replace_int("ctype", intish(last.attribute("ctype")))
|
||||||
lastdict.replace_int('sid', intish(last.attribute('sid')))
|
lastdict.replace_int("sid", intish(last.attribute("sid")))
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Grab character options
|
# Grab character options
|
||||||
chara = request.child('chara')
|
chara = request.child("chara")
|
||||||
if chara is not None:
|
if chara is not None:
|
||||||
newprofile.replace_int('chara', intish(chara.attribute('my')))
|
newprofile.replace_int("chara", intish(chara.attribute("my")))
|
||||||
chara_opt = request.child('chara_opt')
|
chara_opt = request.child("chara_opt")
|
||||||
if chara_opt is not None:
|
if chara_opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Options
|
||||||
opt = request.child('opt')
|
opt = request.child("opt")
|
||||||
if opt is not None:
|
if opt is not None:
|
||||||
# A bug in old versions of AVS returns the wrong number for set
|
# 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
|
# Experience and stars
|
||||||
exp = request.child_value('exp')
|
exp = request.child_value("exp")
|
||||||
if exp is not None:
|
if exp is not None:
|
||||||
play_stats.replace_int('exp', play_stats.get_int('exp') + exp)
|
play_stats.replace_int("exp", play_stats.get_int("exp") + exp)
|
||||||
star = request.child_value('star')
|
star = request.child_value("star")
|
||||||
if star is not None:
|
if star is not None:
|
||||||
newprofile.replace_int('star', newprofile.get_int('star') + star)
|
newprofile.replace_int("star", newprofile.get_int("star") + star)
|
||||||
star_c = request.child_value('star_c')
|
star_c = request.child_value("star_c")
|
||||||
if star_c is not None:
|
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
|
# Update game flags
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'flag':
|
if child.name != "flag":
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
value = int(child.attribute('data'))
|
value = int(child.attribute("data"))
|
||||||
offset = int(child.attribute('no'))
|
offset = int(child.attribute("no"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
continue
|
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):
|
if offset < 0 or offset >= len(flags):
|
||||||
continue
|
continue
|
||||||
flags[offset] = value
|
flags[offset] = value
|
||||||
newprofile.replace_int_array('flag', 256, flags)
|
newprofile.replace_int_array("flag", 256, flags)
|
||||||
|
|
||||||
# Workout mode support
|
# Workout mode support
|
||||||
newweight = -1
|
newweight = -1
|
||||||
oldweight = newprofile.get_int('weight')
|
oldweight = newprofile.get_int("weight")
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'weight':
|
if child.name != "weight":
|
||||||
continue
|
continue
|
||||||
newweight = child.value
|
newweight = child.value
|
||||||
if newweight < 0:
|
if newweight < 0:
|
||||||
@ -700,14 +727,14 @@ class DDRX3(
|
|||||||
# Either update or unset the weight depending on the game
|
# Either update or unset the weight depending on the game
|
||||||
if newweight == 0:
|
if newweight == 0:
|
||||||
# Weight is unset or we declined to use this feature, remove from profile
|
# Weight is unset or we declined to use this feature, remove from profile
|
||||||
if 'weight' in newprofile:
|
if "weight" in newprofile:
|
||||||
del newprofile['weight']
|
del newprofile["weight"]
|
||||||
else:
|
else:
|
||||||
# Weight has been set or previously retrieved, we should save calories
|
# Weight has been set or previously retrieved, we should save calories
|
||||||
newprofile.replace_int('weight', newweight)
|
newprofile.replace_int("weight", newweight)
|
||||||
total = 0
|
total = 0
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'calory':
|
if child.name != "calory":
|
||||||
continue
|
continue
|
||||||
total += child.value
|
total += child.value
|
||||||
self.data.local.user.put_time_based_achievement(
|
self.data.local.user.put_time_based_achievement(
|
||||||
@ -715,10 +742,10 @@ class DDRX3(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
0,
|
0,
|
||||||
'workout',
|
"workout",
|
||||||
{
|
{
|
||||||
'calories': total,
|
"calories": total,
|
||||||
'weight': newweight,
|
"weight": newweight,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -726,7 +753,7 @@ class DDRX3(
|
|||||||
oldfriends: List[Optional[UserID]] = [None] * 10
|
oldfriends: List[Optional[UserID]] = [None] * 10
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type[:7] != 'friend_':
|
if link.type[:7] != "friend_":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pos = int(link.type[7:])
|
pos = int(link.type[7:])
|
||||||
@ -735,11 +762,11 @@ class DDRX3(
|
|||||||
# Save any rivals that were added/removed/changed
|
# Save any rivals that were added/removed/changed
|
||||||
newfriends = oldfriends[:]
|
newfriends = oldfriends[:]
|
||||||
for child in request.children:
|
for child in request.children:
|
||||||
if child.name != 'friend':
|
if child.name != "friend":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
code = int(child.attribute('code'))
|
code = int(child.attribute("code"))
|
||||||
pos = int(child.attribute('pos'))
|
pos = int(child.attribute("pos"))
|
||||||
|
|
||||||
if pos >= 0 and pos < 10:
|
if pos >= 0 and pos < 10:
|
||||||
if code == 0:
|
if code == 0:
|
||||||
@ -747,7 +774,9 @@ class DDRX3(
|
|||||||
newfriends[pos] = None
|
newfriends[pos] = None
|
||||||
else:
|
else:
|
||||||
# Try looking up the userid
|
# 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
|
# Diff the set of links to determine updates
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
@ -760,7 +789,7 @@ class DDRX3(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
elif oldfriends[i] is None:
|
elif oldfriends[i] is None:
|
||||||
@ -769,7 +798,7 @@ class DDRX3(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
@ -779,24 +808,24 @@ class DDRX3(
|
|||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
oldfriends[i],
|
oldfriends[i],
|
||||||
)
|
)
|
||||||
self.data.local.user.put_link(
|
self.data.local.user.put_link(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
f'friend_{i}',
|
f"friend_{i}",
|
||||||
newfriends[i],
|
newfriends[i],
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Play area counter
|
# 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:
|
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
|
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
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid, play_stats)
|
self.update_play_statistics(userid, play_stats)
|
||||||
|
@ -48,12 +48,17 @@ class DDRFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['HDX', 'JDX', 'KDX', 'MDX']:
|
for gamecode in ["HDX", "JDX", "KDX", "MDX"]:
|
||||||
Base.register(gamecode, DDRFactory)
|
Base.register(gamecode, DDRFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date < 2014051200:
|
if date < 2014051200:
|
||||||
return VersionConstants.DDR_2013
|
return VersionConstants.DDR_2013
|
||||||
@ -65,20 +70,20 @@ class DDRFactory(Factory):
|
|||||||
return VersionConstants.DDR_A20
|
return VersionConstants.DDR_A20
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'HDX':
|
if model.gamecode == "HDX":
|
||||||
return DDRX(data, config, model)
|
return DDRX(data, config, model)
|
||||||
if model.gamecode == 'JDX':
|
if model.gamecode == "JDX":
|
||||||
return DDRX2(data, config, model)
|
return DDRX2(data, config, model)
|
||||||
if model.gamecode == 'KDX':
|
if model.gamecode == "KDX":
|
||||||
return DDRX3(data, config, model)
|
return DDRX3(data, config, model)
|
||||||
if model.gamecode == 'MDX':
|
if model.gamecode == "MDX":
|
||||||
if model.version is None:
|
if model.version is None:
|
||||||
if parentmodel is None:
|
if parentmodel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# We have no way to tell apart newer versions. However, we can make
|
# 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.
|
# 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
|
return None
|
||||||
|
|
||||||
parentversion = version_from_date(parentmodel.version)
|
parentversion = version_from_date(parentmodel.version)
|
||||||
|
@ -7,7 +7,7 @@ from bemani.common import VersionConstants
|
|||||||
|
|
||||||
class DDRX(DDRBase):
|
class DDRX(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution X'
|
name: str = "DanceDanceRevolution X"
|
||||||
version: int = VersionConstants.DDR_X
|
version: int = VersionConstants.DDR_X
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -16,7 +16,7 @@ class DDRX(DDRBase):
|
|||||||
|
|
||||||
class DDRSuperNova2(DDRBase):
|
class DDRSuperNova2(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution SuperNova 2'
|
name: str = "DanceDanceRevolution SuperNova 2"
|
||||||
version: int = VersionConstants.DDR_SUPERNOVA_2
|
version: int = VersionConstants.DDR_SUPERNOVA_2
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -25,7 +25,7 @@ class DDRSuperNova2(DDRBase):
|
|||||||
|
|
||||||
class DDRSuperNova(DDRBase):
|
class DDRSuperNova(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution SuperNova'
|
name: str = "DanceDanceRevolution SuperNova"
|
||||||
version: int = VersionConstants.DDR_SUPERNOVA
|
version: int = VersionConstants.DDR_SUPERNOVA
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -34,7 +34,7 @@ class DDRSuperNova(DDRBase):
|
|||||||
|
|
||||||
class DDRExtreme(DDRBase):
|
class DDRExtreme(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution Extreme'
|
name: str = "DanceDanceRevolution Extreme"
|
||||||
version: int = VersionConstants.DDR_EXTREME
|
version: int = VersionConstants.DDR_EXTREME
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -43,7 +43,7 @@ class DDRExtreme(DDRBase):
|
|||||||
|
|
||||||
class DDR7thMix(DDRBase):
|
class DDR7thMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 7thMix'
|
name: str = "DanceDanceRevolution 7thMix"
|
||||||
version: int = VersionConstants.DDR_7THMIX
|
version: int = VersionConstants.DDR_7THMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -52,7 +52,7 @@ class DDR7thMix(DDRBase):
|
|||||||
|
|
||||||
class DDR6thMix(DDRBase):
|
class DDR6thMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 6thMix'
|
name: str = "DanceDanceRevolution 6thMix"
|
||||||
version: int = VersionConstants.DDR_6THMIX
|
version: int = VersionConstants.DDR_6THMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -61,7 +61,7 @@ class DDR6thMix(DDRBase):
|
|||||||
|
|
||||||
class DDR5thMix(DDRBase):
|
class DDR5thMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 5thMix'
|
name: str = "DanceDanceRevolution 5thMix"
|
||||||
version: int = VersionConstants.DDR_5THMIX
|
version: int = VersionConstants.DDR_5THMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -70,7 +70,7 @@ class DDR5thMix(DDRBase):
|
|||||||
|
|
||||||
class DDR4thMix(DDRBase):
|
class DDR4thMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 4thMix'
|
name: str = "DanceDanceRevolution 4thMix"
|
||||||
version: int = VersionConstants.DDR_4THMIX
|
version: int = VersionConstants.DDR_4THMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -79,7 +79,7 @@ class DDR4thMix(DDRBase):
|
|||||||
|
|
||||||
class DDR3rdMix(DDRBase):
|
class DDR3rdMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 3rdMix'
|
name: str = "DanceDanceRevolution 3rdMix"
|
||||||
version: int = VersionConstants.DDR_3RDMIX
|
version: int = VersionConstants.DDR_3RDMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -88,7 +88,7 @@ class DDR3rdMix(DDRBase):
|
|||||||
|
|
||||||
class DDR2ndMix(DDRBase):
|
class DDR2ndMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 2ndMix'
|
name: str = "DanceDanceRevolution 2ndMix"
|
||||||
version: int = VersionConstants.DDR_2NDMIX
|
version: int = VersionConstants.DDR_2NDMIX
|
||||||
|
|
||||||
def previous_version(self) -> Optional[DDRBase]:
|
def previous_version(self) -> Optional[DDRBase]:
|
||||||
@ -97,5 +97,5 @@ class DDR2ndMix(DDRBase):
|
|||||||
|
|
||||||
class DDR1stMix(DDRBase):
|
class DDR1stMix(DDRBase):
|
||||||
|
|
||||||
name: str = 'DanceDanceRevolution 1stMix'
|
name: str = "DanceDanceRevolution 1stMix"
|
||||||
version: int = VersionConstants.DDR_1STMIX
|
version: int = VersionConstants.DDR_1STMIX
|
||||||
|
@ -61,7 +61,7 @@ class Dispatch:
|
|||||||
"""
|
"""
|
||||||
self.log("Received request:\n{}", tree)
|
self.log("Received request:\n{}", tree)
|
||||||
|
|
||||||
if tree.name != 'call':
|
if tree.name != "call":
|
||||||
# Invalid request
|
# Invalid request
|
||||||
self.log("Invalid root node {}", tree.name)
|
self.log("Invalid root node {}", tree.name)
|
||||||
return None
|
return None
|
||||||
@ -71,15 +71,17 @@ class Dispatch:
|
|||||||
self.log("Invalid number of children for root node")
|
self.log("Invalid number of children for root node")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
modelstring = tree.attribute('model')
|
modelstring = tree.attribute("model")
|
||||||
model = Model.from_modelstring(modelstring)
|
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
|
# If we are enforcing, bail out if we don't recognize thie ID
|
||||||
pcb = self.__data.local.machine.get_machine(pcbid)
|
pcb = self.__data.local.machine.get_machine(pcbid)
|
||||||
if self.__config.server.enforce_pcbid and pcb is None:
|
if self.__config.server.enforce_pcbid and pcb is None:
|
||||||
self.log("Unrecognized PCBID {}", pcbid)
|
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 we don't have a Machine, but we aren't enforcing, we must create it
|
||||||
if pcb is None:
|
if pcb is None:
|
||||||
@ -88,9 +90,9 @@ class Dispatch:
|
|||||||
request = tree.children[0]
|
request = tree.children[0]
|
||||||
|
|
||||||
config = self.__config.clone()
|
config = self.__config.clone()
|
||||||
config['machine'] = {
|
config["machine"] = {
|
||||||
'pcbid': pcbid,
|
"pcbid": pcbid,
|
||||||
'arcade': pcb.arcade,
|
"arcade": pcb.arcade,
|
||||||
}
|
}
|
||||||
|
|
||||||
# If the machine we looked up is in an arcade, override the global
|
# 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:
|
if pcb.arcade is not None:
|
||||||
arcade = self.__data.local.machine.get_arcade(pcb.arcade)
|
arcade = self.__data.local.machine.get_arcade(pcb.arcade)
|
||||||
if arcade is not None:
|
if arcade is not None:
|
||||||
config['paseli']['enabled'] = arcade.data.get_bool('paseli_enabled')
|
config["paseli"]["enabled"] = arcade.data.get_bool("paseli_enabled")
|
||||||
config['paseli']['infinite'] = arcade.data.get_bool('paseli_infinite')
|
config["paseli"]["infinite"] = arcade.data.get_bool("paseli_infinite")
|
||||||
if arcade.data.get_bool('mask_services_url'):
|
if arcade.data.get_bool("mask_services_url"):
|
||||||
# Mask the address, no matter what the server settings are
|
# 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)
|
game = Base.create(self.__data, config, model)
|
||||||
method = request.attribute('method')
|
method = request.attribute("method")
|
||||||
response = None
|
response = None
|
||||||
|
|
||||||
# If we are enforcing, make sure the PCBID isn't specified to be
|
# If we are enforcing, make sure the PCBID isn't specified to be
|
||||||
# game-specific
|
# game-specific
|
||||||
if config.server.enforce_pcbid and pcb.game is not None:
|
if config.server.enforce_pcbid and pcb.game is not None:
|
||||||
if pcb.game != game.game:
|
if pcb.game != game.game:
|
||||||
self.log("PCBID {} assigned to game {}, but connected from game {}", pcbid, pcb.game, game.game)
|
self.log(
|
||||||
raise UnrecognizedPCBIDException(pcbid, modelstring, config.client.address)
|
"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 is not None:
|
||||||
if pcb.version > 0 and pcb.version != game.version:
|
if pcb.version > 0 and pcb.version != game.version:
|
||||||
self.log(
|
self.log(
|
||||||
@ -124,7 +133,9 @@ class Dispatch:
|
|||||||
game.game,
|
game.game,
|
||||||
game.version,
|
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:
|
if pcb.version < 0 and (-pcb.version) < game.version:
|
||||||
self.log(
|
self.log(
|
||||||
"PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}",
|
"PCBID {} assigned to game {} maximum version {}, but connected from game {} version {}",
|
||||||
@ -134,11 +145,13 @@ class Dispatch:
|
|||||||
game.game,
|
game.game,
|
||||||
game.version,
|
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
|
# First, try to handle with specific service/method function
|
||||||
try:
|
try:
|
||||||
handler = getattr(game, f'handle_{request.name}_{method}_request')
|
handler = getattr(game, f"handle_{request.name}_{method}_request")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
handler = None
|
handler = None
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
@ -147,7 +160,7 @@ class Dispatch:
|
|||||||
if response is None:
|
if response is None:
|
||||||
# Now, try to pass it off to a generic service handler
|
# Now, try to pass it off to a generic service handler
|
||||||
try:
|
try:
|
||||||
handler = getattr(game, f'handle_{request.name}_requests')
|
handler = getattr(game, f"handle_{request.name}_requests")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
handler = None
|
handler = None
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
@ -159,12 +172,12 @@ class Dispatch:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Make sure we have a status value if one wasn't provided
|
# Make sure we have a status value if one wasn't provided
|
||||||
if 'status' not in response.attributes:
|
if "status" not in response.attributes:
|
||||||
response.set_attribute('status', str(Status.SUCCESS))
|
response.set_attribute("status", str(Status.SUCCESS))
|
||||||
|
|
||||||
root = Node.void('response')
|
root = Node.void("response")
|
||||||
root.add_child(response)
|
root.add_child(response)
|
||||||
root.set_attribute('dstid', pcbid)
|
root.set_attribute("dstid", pcbid)
|
||||||
|
|
||||||
self.log("Sending response:\n{}", root)
|
self.log("Sending response:\n{}", root)
|
||||||
|
|
||||||
|
@ -11,13 +11,13 @@ class EventLogHandler(Base):
|
|||||||
|
|
||||||
def handle_eventlog_write_request(self, request: Node) -> Node:
|
def handle_eventlog_write_request(self, request: Node) -> Node:
|
||||||
# Just turn off further logging
|
# Just turn off further logging
|
||||||
gamesession = request.child_value('data/gamesession')
|
gamesession = request.child_value("data/gamesession")
|
||||||
if gamesession < 0:
|
if gamesession < 0:
|
||||||
gamesession = random.randint(1, 1000000)
|
gamesession = random.randint(1, 1000000)
|
||||||
|
|
||||||
root = Node.void('eventlog')
|
root = Node.void("eventlog")
|
||||||
root.add_child(Node.s64('gamesession', gamesession))
|
root.add_child(Node.s64("gamesession", gamesession))
|
||||||
root.add_child(Node.s32('logsendflg', 0))
|
root.add_child(Node.s32("logsendflg", 0))
|
||||||
root.add_child(Node.s32('logerrlevel', 0))
|
root.add_child(Node.s32("logerrlevel", 0))
|
||||||
root.add_child(Node.s32('evtidnosendflg', 0))
|
root.add_child(Node.s32("evtidnosendflg", 0))
|
||||||
return root
|
return root
|
||||||
|
@ -5,7 +5,14 @@ from typing_extensions import Final
|
|||||||
|
|
||||||
from bemani.backend.base import Base
|
from bemani.backend.base import Base
|
||||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
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.data import Config, Data, Score, Machine, UserID
|
||||||
from bemani.protocol import Node
|
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_CHUDEN: Final[int] = DBConstants.IIDX_DAN_RANK_CHUDEN
|
||||||
DAN_RANK_KAIDEN: Final[int] = DBConstants.IIDX_DAN_RANK_KAIDEN
|
DAN_RANK_KAIDEN: Final[int] = DBConstants.IIDX_DAN_RANK_KAIDEN
|
||||||
|
|
||||||
DAN_RANKING_SINGLE: Final[str] = 'sgrade'
|
DAN_RANKING_SINGLE: Final[str] = "sgrade"
|
||||||
DAN_RANKING_DOUBLE: Final[str] = 'dgrade'
|
DAN_RANKING_DOUBLE: Final[str] = "dgrade"
|
||||||
|
|
||||||
GHOST_TYPE_NONE: Final[int] = 0
|
GHOST_TYPE_NONE: Final[int] = 0
|
||||||
GHOST_TYPE_RIVAL: Final[int] = 100
|
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.
|
# Return the local2 service so that Copula and above will send certain packets.
|
||||||
extra_services: List[str] = [
|
extra_services: List[str] = [
|
||||||
'local2',
|
"local2",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||||
super().__init__(data, config, model)
|
super().__init__(data, config, model)
|
||||||
if model.rev == 'X':
|
if model.rev == "X":
|
||||||
self.omnimix = True
|
self.omnimix = True
|
||||||
else:
|
else:
|
||||||
self.omnimix = False
|
self.omnimix = False
|
||||||
@ -94,7 +101,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||||
return 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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -106,9 +113,11 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
Base handler for a profile. Given a userid and a profile dictionary,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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.
|
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 None
|
||||||
return self.format_profile(userid, profile)
|
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
|
Given a RefID and an optional name, create a profile and then return
|
||||||
that newly created profile.
|
that newly created profile.
|
||||||
@ -145,7 +156,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'なし'
|
name = "なし"
|
||||||
if pid is None:
|
if pid is None:
|
||||||
pid = self.get_machine_region()
|
pid = self.get_machine_region()
|
||||||
|
|
||||||
@ -156,10 +167,10 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
'pid': pid,
|
"pid": pid,
|
||||||
'settings': {
|
"settings": {
|
||||||
'flags': 223 # Default to turning on all optional folders
|
"flags": 223 # Default to turning on all optional folders
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -208,7 +219,8 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
all_attempts, remote_attempts = Parallel.execute([
|
all_attempts, remote_attempts = Parallel.execute(
|
||||||
|
[
|
||||||
lambda: self.data.local.music.get_all_attempts(
|
lambda: self.data.local.music.get_all_attempts(
|
||||||
game=self.game,
|
game=self.game,
|
||||||
version=self.music_version,
|
version=self.music_version,
|
||||||
@ -221,11 +233,12 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
songid=songid,
|
songid=songid,
|
||||||
songchart=songchart,
|
songchart=songchart,
|
||||||
),
|
),
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||||
for (_, attempt) in all_attempts:
|
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.
|
# This attempt was outside of the clear infra, so don't bother with it.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -234,24 +247,33 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
attempts[attempt.id] = {}
|
attempts[attempt.id] = {}
|
||||||
if attempt.chart not in attempts[attempt.id]:
|
if attempt.chart not in attempts[attempt.id]:
|
||||||
attempts[attempt.id][attempt.chart] = {
|
attempts[attempt.id][attempt.chart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'fcs': 0,
|
"fcs": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
# We saw an attempt, keep the total attempts in sync.
|
# 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
|
# This attempt was a failure, so don't count it against clears of full combos
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# It was at least a clear
|
# 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
|
# 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
|
# Merge in remote attempts
|
||||||
for songid in remote_attempts:
|
for songid in remote_attempts:
|
||||||
@ -261,14 +283,20 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
for songchart in remote_attempts[songid]:
|
for songchart in remote_attempts[songid]:
|
||||||
if songchart not in attempts[songid]:
|
if songchart not in attempts[songid]:
|
||||||
attempts[songid][songchart] = {
|
attempts[songid][songchart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'fcs': 0,
|
"fcs": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
songchart
|
||||||
attempts[songid][songchart]['fcs'] += remote_attempts[songid][songchart]['combos']
|
]["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 requesting a specific song/chart, make sure its in the dict
|
||||||
if songid is not None:
|
if songid is not None:
|
||||||
@ -278,9 +306,9 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
if songchart is not None:
|
if songchart is not None:
|
||||||
if songchart not in attempts[songid]:
|
if songchart not in attempts[songid]:
|
||||||
attempts[songid][songchart] = {
|
attempts[songid][songchart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'fcs': 0,
|
"fcs": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
return attempts
|
return attempts
|
||||||
@ -339,26 +367,30 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
oldscore = None
|
oldscore = None
|
||||||
|
|
||||||
# Score history is verbatum, instead of highest score
|
# Score history is verbatum, instead of highest score
|
||||||
history = ValidatedDict({
|
history = ValidatedDict(
|
||||||
'clear_status': clear_status,
|
{
|
||||||
'miss_count': miss_count,
|
"clear_status": clear_status,
|
||||||
})
|
"miss_count": miss_count,
|
||||||
|
}
|
||||||
|
)
|
||||||
old_ex_score = ex_score
|
old_ex_score = ex_score
|
||||||
|
|
||||||
if ghost is not None:
|
if ghost is not None:
|
||||||
history['ghost'] = ghost
|
history["ghost"] = ghost
|
||||||
|
|
||||||
if oldscore is None:
|
if oldscore is None:
|
||||||
# If it is a new score, create a new dictionary to add to
|
# If it is a new score, create a new dictionary to add to
|
||||||
scoredata = ValidatedDict({
|
scoredata = ValidatedDict(
|
||||||
'clear_status': clear_status,
|
{
|
||||||
'pgreats': pgreats,
|
"clear_status": clear_status,
|
||||||
'greats': greats,
|
"pgreats": pgreats,
|
||||||
})
|
"greats": greats,
|
||||||
|
}
|
||||||
|
)
|
||||||
if miss_count != -1:
|
if miss_count != -1:
|
||||||
scoredata.replace_int('miss_count', miss_count)
|
scoredata.replace_int("miss_count", miss_count)
|
||||||
if ghost is not None:
|
if ghost is not None:
|
||||||
scoredata['ghost'] = ghost
|
scoredata["ghost"] = ghost
|
||||||
raised = True
|
raised = True
|
||||||
highscore = True
|
highscore = True
|
||||||
else:
|
else:
|
||||||
@ -367,21 +399,25 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
highscore = ex_score >= oldscore.points
|
highscore = ex_score >= oldscore.points
|
||||||
ex_score = max(ex_score, oldscore.points)
|
ex_score = max(ex_score, oldscore.points)
|
||||||
scoredata = oldscore.data
|
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 miss_count != -1:
|
||||||
if scoredata.get_int('miss_count', -1) == -1:
|
if scoredata.get_int("miss_count", -1) == -1:
|
||||||
scoredata.replace_int('miss_count', miss_count)
|
scoredata.replace_int("miss_count", miss_count)
|
||||||
else:
|
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:
|
if raised:
|
||||||
scoredata.replace_int('pgreats', pgreats)
|
scoredata.replace_int("pgreats", pgreats)
|
||||||
scoredata.replace_int('greats', greats)
|
scoredata.replace_int("greats", greats)
|
||||||
if ghost is not None:
|
if ghost is not None:
|
||||||
scoredata.replace_bytes('ghost', ghost)
|
scoredata.replace_bytes("ghost", ghost)
|
||||||
|
|
||||||
if shop is not None:
|
if shop is not None:
|
||||||
history.replace_int('shop', shop)
|
history.replace_int("shop", shop)
|
||||||
scoredata.replace_int('shop', shop)
|
scoredata.replace_int("shop", shop)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
@ -477,42 +513,41 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
)
|
)
|
||||||
if dan_score is None:
|
if dan_score is None:
|
||||||
dan_score = ValidatedDict()
|
dan_score = ValidatedDict()
|
||||||
dan_score.replace_int('percent', max(percent, dan_score.get_int('percent')))
|
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(
|
||||||
|
"stages_cleared", max(stages_cleared, dan_score.get_int("stages_cleared"))
|
||||||
|
)
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game, self.version, userid, rank, dantype, dan_score
|
||||||
self.version,
|
|
||||||
userid,
|
|
||||||
rank,
|
|
||||||
dantype,
|
|
||||||
dan_score
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def db_to_game_status(self, db_status: int) -> int:
|
def db_to_game_status(self, db_status: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a DB status, translate to a game clear status.
|
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:
|
def game_to_db_status(self, game_status: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a game clear status, translate to DB status.
|
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:
|
def game_to_db_chart(self, game_chart: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a game's chart for a song, return the chart as defined above.
|
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:
|
def db_to_game_chart(self, db_chart: int) -> int:
|
||||||
"""
|
"""
|
||||||
Given a chart as defined above, return the game's chart constant.
|
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]] = {}
|
scorestruct: Dict[int, List[int]] = {}
|
||||||
|
|
||||||
for score in scores:
|
for score in scores:
|
||||||
@ -560,9 +595,11 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
-1, # Miss count another,
|
-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 + 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]
|
return [scorestruct[s] for s in scorestruct]
|
||||||
|
|
||||||
@ -577,10 +614,12 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
if chart != self.CHART_TYPE_B7:
|
if chart != self.CHART_TYPE_B7:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
scorelist.append([
|
scorelist.append(
|
||||||
|
[
|
||||||
musicid,
|
musicid,
|
||||||
self.db_to_game_status(score.data.get_int('clear_status')),
|
self.db_to_game_status(score.data.get_int("clear_status")),
|
||||||
])
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return scorelist
|
return scorelist
|
||||||
|
|
||||||
@ -597,7 +636,7 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
# Sum up for each bucket
|
# Sum up for each bucket
|
||||||
for score in scores:
|
for score in scores:
|
||||||
ghost = score.data.get_bytes('ghost')
|
ghost = score.data.get_bytes("ghost")
|
||||||
for i in range(len(ghost)):
|
for i in range(len(ghost)):
|
||||||
total_ghost[i] = total_ghost[i] + ghost[i]
|
total_ghost[i] = total_ghost[i] + ghost[i]
|
||||||
count = count + 1
|
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)]
|
delta_ghost = [total_ghost[i] - reference_ghost[i] for i in range(ghost_length)]
|
||||||
|
|
||||||
# Return averages
|
# 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:
|
def user_joined_arcade(self, machine: Machine, profile: Optional[Profile]) -> bool:
|
||||||
if profile is None:
|
if profile is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if 'shop_location' not in profile:
|
if "shop_location" not in profile:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
machineid = profile.get_int('shop_location')
|
machineid = profile.get_int("shop_location")
|
||||||
if machineid == machine.id:
|
if machineid == machine.id:
|
||||||
# We can short-circuit arcade lookup because their machine
|
# We can short-circuit arcade lookup because their machine
|
||||||
# is the current machine.
|
# is the current machine.
|
||||||
@ -659,30 +698,39 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
if ghost_type == self.GHOST_TYPE_RIVAL:
|
if ghost_type == self.GHOST_TYPE_RIVAL:
|
||||||
rival_extid = int(parameter)
|
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:
|
if rival_userid is not None:
|
||||||
rival_profile = self.get_profile(rival_userid)
|
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:
|
if rival_score is not None and rival_profile is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': rival_score.points,
|
"score": rival_score.points,
|
||||||
'ghost': rival_score.data.get_bytes('ghost'),
|
"ghost": rival_score.data.get_bytes("ghost"),
|
||||||
'name': rival_profile.get_str('name'),
|
"name": rival_profile.get_str("name"),
|
||||||
'pid': rival_profile.get_int('pid'),
|
"pid": rival_profile.get_int("pid"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP or
|
ghost_type == self.GHOST_TYPE_GLOBAL_TOP
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP or
|
or ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||||
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE or
|
or ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
or ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||||
):
|
):
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP or
|
ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
or ghost_type == self.GHOST_TYPE_LOCAL_AVERAGE
|
||||||
):
|
):
|
||||||
all_scores = sorted(
|
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,
|
key=lambda s: s[1].points,
|
||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
@ -698,30 +746,38 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'shop_location' in my_profile:
|
if "shop_location" in my_profile:
|
||||||
shop_id = my_profile.get_int('shop_location')
|
shop_id = my_profile.get_int("shop_location")
|
||||||
machine = self.get_machine_by_id(shop_id)
|
machine = self.get_machine_by_id(shop_id)
|
||||||
else:
|
else:
|
||||||
machine = None
|
machine = None
|
||||||
|
|
||||||
if machine is not None:
|
if machine is not None:
|
||||||
all_scores = [
|
all_scores = [
|
||||||
score for score in all_scores
|
score
|
||||||
if self.user_joined_arcade(machine, self.get_any_profile(score[0]))
|
for score in all_scores
|
||||||
|
if self.user_joined_arcade(
|
||||||
|
machine, self.get_any_profile(score[0])
|
||||||
|
)
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
# Not joined an arcade, so nobody matches our scores
|
# Not joined an arcade, so nobody matches our scores
|
||||||
all_scores = []
|
all_scores = []
|
||||||
else:
|
else:
|
||||||
all_scores = sorted(
|
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,
|
key=lambda s: s[1].points,
|
||||||
reverse=True,
|
reverse=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_GLOBAL_TOP or
|
ghost_type == self.GHOST_TYPE_GLOBAL_TOP
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
or ghost_type == self.GHOST_TYPE_LOCAL_TOP
|
||||||
):
|
):
|
||||||
for potential_top in all_scores:
|
for potential_top in all_scores:
|
||||||
top_userid = potential_top[0]
|
top_userid = potential_top[0]
|
||||||
@ -729,28 +785,30 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
top_profile = self.get_any_profile(top_userid)
|
top_profile = self.get_any_profile(top_userid)
|
||||||
if top_profile is not None:
|
if top_profile is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': top_score.points,
|
"score": top_score.points,
|
||||||
'ghost': top_score.data.get_bytes('ghost'),
|
"ghost": top_score.data.get_bytes("ghost"),
|
||||||
'name': top_profile.get_str('name'),
|
"name": top_profile.get_str("name"),
|
||||||
'pid': top_profile.get_int('pid'),
|
"pid": top_profile.get_int("pid"),
|
||||||
'extid': top_profile.extid,
|
"extid": top_profile.extid,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE or
|
ghost_type == self.GHOST_TYPE_GLOBAL_AVERAGE
|
||||||
ghost_type == self.GHOST_TYPE_LOCAL_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:
|
if average_score is not None and delta_ghost is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': average_score,
|
"score": average_score,
|
||||||
'ghost': bytes([0] * ghost_length),
|
"ghost": bytes([0] * ghost_length),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_DAN_TOP or
|
ghost_type == self.GHOST_TYPE_DAN_TOP
|
||||||
ghost_type == self.GHOST_TYPE_DAN_AVERAGE
|
or ghost_type == self.GHOST_TYPE_DAN_AVERAGE
|
||||||
):
|
):
|
||||||
is_dp = chart not in [
|
is_dp = chart not in [
|
||||||
self.CHART_TYPE_N7,
|
self.CHART_TYPE_N7,
|
||||||
@ -772,18 +830,28 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
if dan_rank != -1:
|
if dan_rank != -1:
|
||||||
all_scores = sorted(
|
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,
|
key=lambda s: s[1].points,
|
||||||
reverse=True,
|
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 = {
|
relevant_userids = {
|
||||||
profile[0] for profile in all_profiles
|
profile[0]
|
||||||
if profile[1].get_int(self.DAN_RANKING_DOUBLE if is_dp else self.DAN_RANKING_SINGLE) == dan_rank
|
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 = [
|
relevant_scores = [
|
||||||
score for score in all_scores
|
score for score in all_scores if score[0] in relevant_userids
|
||||||
if score[0] in relevant_userids
|
|
||||||
]
|
]
|
||||||
if ghost_type == self.GHOST_TYPE_DAN_TOP:
|
if ghost_type == self.GHOST_TYPE_DAN_TOP:
|
||||||
for potential_top in relevant_scores:
|
for potential_top in relevant_scores:
|
||||||
@ -792,27 +860,29 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
top_profile = self.get_any_profile(top_userid)
|
top_profile = self.get_any_profile(top_userid)
|
||||||
if top_profile is not None:
|
if top_profile is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': top_score.points,
|
"score": top_score.points,
|
||||||
'ghost': top_score.data.get_bytes('ghost'),
|
"ghost": top_score.data.get_bytes("ghost"),
|
||||||
'name': top_profile.get_str('name'),
|
"name": top_profile.get_str("name"),
|
||||||
'pid': top_profile.get_int('pid'),
|
"pid": top_profile.get_int("pid"),
|
||||||
'extid': top_profile.extid,
|
"extid": top_profile.extid,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
if ghost_type == self.GHOST_TYPE_DAN_AVERAGE:
|
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:
|
if average_score is not None and delta_ghost is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': average_score,
|
"score": average_score,
|
||||||
'ghost': bytes([0] * ghost_length),
|
"ghost": bytes([0] * ghost_length),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
ghost_type == self.GHOST_TYPE_RIVAL_TOP or
|
ghost_type == self.GHOST_TYPE_RIVAL_TOP
|
||||||
ghost_type == self.GHOST_TYPE_RIVAL_AVERAGE
|
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 = [
|
rival_userids = [
|
||||||
self.data.remote.user.from_extid(self.game, self.version, rival_extid)
|
self.data.remote.user.from_extid(self.game, self.version, rival_extid)
|
||||||
for rival_extid in rival_extids
|
for rival_extid in rival_extids
|
||||||
@ -820,8 +890,13 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
all_scores = sorted(
|
all_scores = sorted(
|
||||||
[
|
[
|
||||||
score for score in
|
score
|
||||||
self.data.remote.music.get_all_scores(game=self.game, version=self.music_version, songid=musicid, songchart=chart)
|
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
|
if score[0] in rival_userids
|
||||||
],
|
],
|
||||||
key=lambda s: s[1].points,
|
key=lambda s: s[1].points,
|
||||||
@ -834,20 +909,22 @@ class IIDXBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
top_profile = self.get_any_profile(top_userid)
|
top_profile = self.get_any_profile(top_userid)
|
||||||
if top_profile is not None:
|
if top_profile is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': top_score.points,
|
"score": top_score.points,
|
||||||
'ghost': top_score.data.get_bytes('ghost'),
|
"ghost": top_score.data.get_bytes("ghost"),
|
||||||
'name': top_profile.get_str('name'),
|
"name": top_profile.get_str("name"),
|
||||||
'pid': top_profile.get_int('pid'),
|
"pid": top_profile.get_int("pid"),
|
||||||
'extid': top_profile.extid,
|
"extid": top_profile.extid,
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
if ghost_type == self.GHOST_TYPE_RIVAL_AVERAGE:
|
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:
|
if average_score is not None and delta_ghost is not None:
|
||||||
ghost_score = {
|
ghost_score = {
|
||||||
'score': average_score,
|
"score": average_score,
|
||||||
'ghost': bytes([0] * ghost_length),
|
"ghost": bytes([0] * ghost_length),
|
||||||
}
|
}
|
||||||
|
|
||||||
return ghost_score
|
return ghost_score
|
||||||
|
@ -8,7 +8,7 @@ from bemani.common import VersionConstants
|
|||||||
|
|
||||||
class IIDXBistrover(IIDXBase):
|
class IIDXBistrover(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX BISTROVER'
|
name: str = "Beatmania IIDX BISTROVER"
|
||||||
version: int = VersionConstants.IIDX_BISTROVER
|
version: int = VersionConstants.IIDX_BISTROVER
|
||||||
|
|
||||||
requires_extended_regions: bool = True
|
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):
|
class IIDXCourse(IIDXBase):
|
||||||
|
|
||||||
COURSE_TYPE_SECRET: Final[str] = 'secret_course'
|
COURSE_TYPE_SECRET: Final[str] = "secret_course"
|
||||||
COURSE_TYPE_INTERNET_RANKING: Final[str] = 'ir_course'
|
COURSE_TYPE_INTERNET_RANKING: Final[str] = "ir_course"
|
||||||
COURSE_TYPE_CLASSIC: Final[str] = 'classic_course'
|
COURSE_TYPE_CLASSIC: Final[str] = "classic_course"
|
||||||
|
|
||||||
def id_and_chart_from_courseid(self, courseid: int) -> Tuple[int, int]:
|
def id_and_chart_from_courseid(self, courseid: int) -> Tuple[int, int]:
|
||||||
return (int(courseid / 6), courseid % 6)
|
return (int(courseid / 6), courseid % 6)
|
||||||
@ -57,11 +57,15 @@ class IIDXCourse(IIDXBase):
|
|||||||
)
|
)
|
||||||
if course_score is None:
|
if course_score is None:
|
||||||
course_score = ValidatedDict()
|
course_score = ValidatedDict()
|
||||||
course_score.replace_int('clear_status', max(clear_status, course_score.get_int('clear_status')))
|
course_score.replace_int(
|
||||||
old_ex_score = (course_score.get_int('pgnum') * 2) + course_score.get_int('gnum')
|
"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):
|
if old_ex_score < ((pgreats * 2) + greats):
|
||||||
course_score.replace_int('pgnum', pgreats)
|
course_score.replace_int("pgnum", pgreats)
|
||||||
course_score.replace_int('gnum', greats)
|
course_score.replace_int("gnum", greats)
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
|
@ -70,12 +70,17 @@ class IIDXFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['JDJ', 'JDZ', 'KDZ', 'LDJ']:
|
for gamecode in ["JDJ", "JDZ", "KDZ", "LDJ"]:
|
||||||
Base.register(gamecode, IIDXFactory)
|
Base.register(gamecode, IIDXFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date < 2013100200:
|
if date < 2013100200:
|
||||||
return VersionConstants.IIDX_TRICORO
|
return VersionConstants.IIDX_TRICORO
|
||||||
@ -97,20 +102,20 @@ class IIDXFactory(Factory):
|
|||||||
return VersionConstants.IIDX_BISTROVER
|
return VersionConstants.IIDX_BISTROVER
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'JDJ':
|
if model.gamecode == "JDJ":
|
||||||
return IIDXSirius(data, config, model)
|
return IIDXSirius(data, config, model)
|
||||||
if model.gamecode == 'JDZ':
|
if model.gamecode == "JDZ":
|
||||||
return IIDXResortAnthem(data, config, model)
|
return IIDXResortAnthem(data, config, model)
|
||||||
if model.gamecode == 'KDZ':
|
if model.gamecode == "KDZ":
|
||||||
return IIDXLincle(data, config, model)
|
return IIDXLincle(data, config, model)
|
||||||
if model.gamecode == 'LDJ':
|
if model.gamecode == "LDJ":
|
||||||
if model.version is None:
|
if model.version is None:
|
||||||
if parentmodel is None:
|
if parentmodel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# We have no way to tell apart newer versions. However, we can make
|
# 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.
|
# 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
|
return None
|
||||||
parentversion = version_from_date(parentmodel.version)
|
parentversion = version_from_date(parentmodel.version)
|
||||||
if parentversion == VersionConstants.IIDX_SPADA:
|
if parentversion == VersionConstants.IIDX_SPADA:
|
||||||
|
@ -8,7 +8,7 @@ from bemani.common import VersionConstants
|
|||||||
|
|
||||||
class IIDXHeroicVerse(IIDXBase):
|
class IIDXHeroicVerse(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX HEROIC VERSE'
|
name: str = "Beatmania IIDX HEROIC VERSE"
|
||||||
version: int = VersionConstants.IIDX_HEROIC_VERSE
|
version: int = VersionConstants.IIDX_HEROIC_VERSE
|
||||||
|
|
||||||
requires_extended_regions: bool = True
|
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):
|
class IIDX1stStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 1st style & substream'
|
name: str = "Beatmania IIDX 1st style & substream"
|
||||||
version: int = VersionConstants.IIDX
|
version: int = VersionConstants.IIDX
|
||||||
|
|
||||||
|
|
||||||
class IIDX2ndStyle(IIDXBase):
|
class IIDX2ndStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 2nd style'
|
name: str = "Beatmania IIDX 2nd style"
|
||||||
version: int = VersionConstants.IIDX_2ND_STYLE
|
version: int = VersionConstants.IIDX_2ND_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -22,7 +22,7 @@ class IIDX2ndStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX3rdStyle(IIDXBase):
|
class IIDX3rdStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 3rd style'
|
name: str = "Beatmania IIDX 3rd style"
|
||||||
version: int = VersionConstants.IIDX_3RD_STYLE
|
version: int = VersionConstants.IIDX_3RD_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -31,7 +31,7 @@ class IIDX3rdStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX4thStyle(IIDXBase):
|
class IIDX4thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 4th style'
|
name: str = "Beatmania IIDX 4th style"
|
||||||
version: int = VersionConstants.IIDX_4TH_STYLE
|
version: int = VersionConstants.IIDX_4TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -40,7 +40,7 @@ class IIDX4thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX5thStyle(IIDXBase):
|
class IIDX5thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 5th style'
|
name: str = "Beatmania IIDX 5th style"
|
||||||
version: int = VersionConstants.IIDX_5TH_STYLE
|
version: int = VersionConstants.IIDX_5TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -49,7 +49,7 @@ class IIDX5thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX6thStyle(IIDXBase):
|
class IIDX6thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 6th style'
|
name: str = "Beatmania IIDX 6th style"
|
||||||
version: int = VersionConstants.IIDX_6TH_STYLE
|
version: int = VersionConstants.IIDX_6TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -58,7 +58,7 @@ class IIDX6thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX7thStyle(IIDXBase):
|
class IIDX7thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 7th style'
|
name: str = "Beatmania IIDX 7th style"
|
||||||
version: int = VersionConstants.IIDX_7TH_STYLE
|
version: int = VersionConstants.IIDX_7TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -67,7 +67,7 @@ class IIDX7thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX8thStyle(IIDXBase):
|
class IIDX8thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 8th style'
|
name: str = "Beatmania IIDX 8th style"
|
||||||
version: int = VersionConstants.IIDX_8TH_STYLE
|
version: int = VersionConstants.IIDX_8TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -76,7 +76,7 @@ class IIDX8thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX9thStyle(IIDXBase):
|
class IIDX9thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 9th style'
|
name: str = "Beatmania IIDX 9th style"
|
||||||
version: int = VersionConstants.IIDX_9TH_STYLE
|
version: int = VersionConstants.IIDX_9TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -85,7 +85,7 @@ class IIDX9thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDX10thStyle(IIDXBase):
|
class IIDX10thStyle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX 10th style'
|
name: str = "Beatmania IIDX 10th style"
|
||||||
version: int = VersionConstants.IIDX_10TH_STYLE
|
version: int = VersionConstants.IIDX_10TH_STYLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -94,7 +94,7 @@ class IIDX10thStyle(IIDXBase):
|
|||||||
|
|
||||||
class IIDXRed(IIDXBase):
|
class IIDXRed(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX RED'
|
name: str = "Beatmania IIDX RED"
|
||||||
version: int = VersionConstants.IIDX_RED
|
version: int = VersionConstants.IIDX_RED
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -103,7 +103,7 @@ class IIDXRed(IIDXBase):
|
|||||||
|
|
||||||
class IIDXHappySky(IIDXBase):
|
class IIDXHappySky(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX HAPPY SKY'
|
name: str = "Beatmania IIDX HAPPY SKY"
|
||||||
version: int = VersionConstants.IIDX_HAPPY_SKY
|
version: int = VersionConstants.IIDX_HAPPY_SKY
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -112,7 +112,7 @@ class IIDXHappySky(IIDXBase):
|
|||||||
|
|
||||||
class IIDXDistorted(IIDXBase):
|
class IIDXDistorted(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX DistorteD'
|
name: str = "Beatmania IIDX DistorteD"
|
||||||
version: int = VersionConstants.IIDX_DISTORTED
|
version: int = VersionConstants.IIDX_DISTORTED
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -121,7 +121,7 @@ class IIDXDistorted(IIDXBase):
|
|||||||
|
|
||||||
class IIDXGold(IIDXBase):
|
class IIDXGold(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX GOLD'
|
name: str = "Beatmania IIDX GOLD"
|
||||||
version: int = VersionConstants.IIDX_GOLD
|
version: int = VersionConstants.IIDX_GOLD
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -130,7 +130,7 @@ class IIDXGold(IIDXBase):
|
|||||||
|
|
||||||
class IIDXDJTroopers(IIDXBase):
|
class IIDXDJTroopers(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX DJ TROOPERS'
|
name: str = "Beatmania IIDX DJ TROOPERS"
|
||||||
version: int = VersionConstants.IIDX_DJ_TROOPERS
|
version: int = VersionConstants.IIDX_DJ_TROOPERS
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -139,7 +139,7 @@ class IIDXDJTroopers(IIDXBase):
|
|||||||
|
|
||||||
class IIDXEmpress(IIDXBase):
|
class IIDXEmpress(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX EMPRESS'
|
name: str = "Beatmania IIDX EMPRESS"
|
||||||
version: int = VersionConstants.IIDX_EMPRESS
|
version: int = VersionConstants.IIDX_EMPRESS
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -148,7 +148,7 @@ class IIDXEmpress(IIDXBase):
|
|||||||
|
|
||||||
class IIDXSirius(IIDXBase):
|
class IIDXSirius(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX SIRIUS'
|
name: str = "Beatmania IIDX SIRIUS"
|
||||||
version: int = VersionConstants.IIDX_SIRIUS
|
version: int = VersionConstants.IIDX_SIRIUS
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -157,7 +157,7 @@ class IIDXSirius(IIDXBase):
|
|||||||
|
|
||||||
class IIDXResortAnthem(IIDXBase):
|
class IIDXResortAnthem(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX Resort Anthem'
|
name: str = "Beatmania IIDX Resort Anthem"
|
||||||
version: int = VersionConstants.IIDX_RESORT_ANTHEM
|
version: int = VersionConstants.IIDX_RESORT_ANTHEM
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
def previous_version(self) -> Optional[IIDXBase]:
|
||||||
@ -166,7 +166,7 @@ class IIDXResortAnthem(IIDXBase):
|
|||||||
|
|
||||||
class IIDXLincle(IIDXBase):
|
class IIDXLincle(IIDXBase):
|
||||||
|
|
||||||
name: str = 'Beatmania IIDX Lincle'
|
name: str = "Beatmania IIDX Lincle"
|
||||||
version: int = VersionConstants.IIDX_LINCLE
|
version: int = VersionConstants.IIDX_LINCLE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[IIDXBase]:
|
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):
|
class JubeatAvenue(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Avenue'
|
name: str = "Jubeat Avenue"
|
||||||
version: int = VersionConstants.JUBEAT_AVENUE
|
version: int = VersionConstants.JUBEAT_AVENUE
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
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_FAILED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_FAILED
|
||||||
PLAY_MEDAL_CLEARED: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_CLEARED
|
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_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
|
PLAY_MEDAL_EXCELLENT: Final[int] = DBConstants.JUBEAT_PLAY_MEDAL_EXCELLENT
|
||||||
|
|
||||||
CHART_TYPE_BASIC: Final[int] = 0
|
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:
|
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||||
super().__init__(data, config, model)
|
super().__init__(data, config, model)
|
||||||
if model.rev == 'X' or model.rev == 'Y':
|
if model.rev == "X" or model.rev == "Y":
|
||||||
self.omnimix = True
|
self.omnimix = True
|
||||||
else:
|
else:
|
||||||
self.omnimix = False
|
self.omnimix = False
|
||||||
@ -53,7 +57,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||||
return 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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -69,8 +73,8 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
userid - The user ID we are saving the profile for.
|
userid - The user ID we are saving the profile for.
|
||||||
profile - A dictionary that should be looked up later using get_profile.
|
profile - A dictionary that should be looked up later using get_profile.
|
||||||
"""
|
"""
|
||||||
if 'has_old_version' in profile:
|
if "has_old_version" in profile:
|
||||||
del profile['has_old_version']
|
del profile["has_old_version"]
|
||||||
super().put_profile(userid, profile)
|
super().put_profile(userid, profile)
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
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,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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 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,
|
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.
|
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
|
# Now try to find out if the profile is new or old
|
||||||
oldversion = self.previous_version()
|
oldversion = self.previous_version()
|
||||||
oldprofile = oldversion.get_profile(userid)
|
oldprofile = oldversion.get_profile(userid)
|
||||||
profile['has_old_version'] = oldprofile is not None
|
profile["has_old_version"] = oldprofile is not None
|
||||||
|
|
||||||
# Now, return it
|
# Now, return it
|
||||||
return self.format_profile(userid, profile)
|
return self.format_profile(userid, profile)
|
||||||
@ -127,7 +135,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'なし'
|
name = "なし"
|
||||||
|
|
||||||
# First, create and save the default profile
|
# First, create and save the default profile
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -137,7 +145,7 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.put_profile(userid, profile)
|
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
|
# Now, reload and format the profile, looking up the has old version flag
|
||||||
oldversion = self.previous_version()
|
oldversion = self.previous_version()
|
||||||
oldprofile = oldversion.get_profile(userid)
|
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)
|
return self.format_profile(userid, profile)
|
||||||
|
|
||||||
@ -158,7 +166,9 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
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:
|
if scores is None:
|
||||||
return None
|
return None
|
||||||
profile = self.get_profile(userid)
|
profile = self.get_profile(userid)
|
||||||
@ -220,38 +230,38 @@ class JubeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Replace medal with highest value
|
# Replace medal with highest value
|
||||||
scoredata.replace_int('medal', max(scoredata.get_int('medal'), medal))
|
scoredata.replace_int("medal", max(scoredata.get_int("medal"), medal))
|
||||||
history.replace_int('medal', medal)
|
history.replace_int("medal", medal)
|
||||||
|
|
||||||
# Increment counters based on medal
|
# Increment counters based on medal
|
||||||
if medal == self.PLAY_MEDAL_CLEARED:
|
if medal == self.PLAY_MEDAL_CLEARED:
|
||||||
scoredata.increment_int('clear_count')
|
scoredata.increment_int("clear_count")
|
||||||
if medal == self.PLAY_MEDAL_FULL_COMBO:
|
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:
|
if medal == self.PLAY_MEDAL_EXCELLENT:
|
||||||
scoredata.increment_int('excellent_count')
|
scoredata.increment_int("excellent_count")
|
||||||
|
|
||||||
# If we have a combo, replace it
|
# If we have a combo, replace it
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
|
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
if raised:
|
if raised:
|
||||||
# We have stats, and there's a new high score, update the stats
|
# We have stats, and there's a new high score, update the stats
|
||||||
scoredata.replace_dict('stats', stats)
|
scoredata.replace_dict("stats", stats)
|
||||||
history.replace_dict('stats', stats)
|
history.replace_dict("stats", stats)
|
||||||
|
|
||||||
if ghost is not None:
|
if ghost is not None:
|
||||||
# Update the ghost regardless, but don't bother with it in history
|
# 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 music_rate is not None:
|
||||||
if oldscore is not None:
|
if oldscore is not None:
|
||||||
if music_rate > oldscore.data.get_int('music_rate'):
|
if music_rate > oldscore.data.get_int("music_rate"):
|
||||||
scoredata.replace_int('music_rate', music_rate)
|
scoredata.replace_int("music_rate", music_rate)
|
||||||
else:
|
else:
|
||||||
scoredata.replace_int('music_rate', music_rate)
|
scoredata.replace_int("music_rate", music_rate)
|
||||||
history.replace_int('music_rate', music_rate)
|
history.replace_int("music_rate", music_rate)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
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):
|
class JubeatLoggerReportHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_logger_report_request(self, request: Node) -> Node:
|
def handle_logger_report_request(self, request: Node) -> Node:
|
||||||
# Handle this by returning nothing, game doesn't care
|
# Handle this by returning nothing, game doesn't care
|
||||||
root = Node.void('logger')
|
root = Node.void("logger")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
class JubeatDemodataGetNewsHandler(JubeatBase):
|
class JubeatDemodataGetNewsHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_demodata_get_news_request(self, request: Node) -> Node:
|
def handle_demodata_get_news_request(self, request: Node) -> Node:
|
||||||
demodata = Node.void('demodata')
|
demodata = Node.void("demodata")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
demodata.add_child(data)
|
demodata.add_child(data)
|
||||||
|
|
||||||
officialnews = Node.void('officialnews')
|
officialnews = Node.void("officialnews")
|
||||||
data.add_child(officialnews)
|
data.add_child(officialnews)
|
||||||
officialnews.set_attribute('count', '0')
|
officialnews.set_attribute("count", "0")
|
||||||
|
|
||||||
return demodata
|
return demodata
|
||||||
|
|
||||||
|
|
||||||
class JubeatDemodataGetHitchartHandler(JubeatBase):
|
class JubeatDemodataGetHitchartHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_demodata_get_hitchart_request(self, request: Node) -> Node:
|
def handle_demodata_get_hitchart_request(self, request: Node) -> Node:
|
||||||
demodata = Node.void('demodata')
|
demodata = Node.void("demodata")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
demodata.add_child(data)
|
demodata.add_child(data)
|
||||||
|
|
||||||
# Not sure what this is, maybe date?
|
# 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
|
# No idea which songs are licensed or regular, so only return hit chart
|
||||||
# for all songs on regular mode.
|
# for all songs on regular mode.
|
||||||
hitchart_lic = Node.void('hitchart_lic')
|
hitchart_lic = Node.void("hitchart_lic")
|
||||||
data.add_child(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)
|
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)
|
data.add_child(hitchart_org)
|
||||||
hitchart_org.set_attribute('count', str(len(songs)))
|
hitchart_org.set_attribute("count", str(len(songs)))
|
||||||
rank = 1
|
rank = 1
|
||||||
for song in songs:
|
for song in songs:
|
||||||
rankdata = Node.void('rankdata')
|
rankdata = Node.void("rankdata")
|
||||||
hitchart_org.add_child(rankdata)
|
hitchart_org.add_child(rankdata)
|
||||||
rankdata.add_child(Node.s32('music_id', song[0]))
|
rankdata.add_child(Node.s32("music_id", song[0]))
|
||||||
rankdata.add_child(Node.s16('rank', rank))
|
rankdata.add_child(Node.s16("rank", rank))
|
||||||
rankdata.add_child(Node.s16('prev', rank))
|
rankdata.add_child(Node.s16("prev", rank))
|
||||||
rank = rank + 1
|
rank = rank + 1
|
||||||
|
|
||||||
return demodata
|
return demodata
|
||||||
|
|
||||||
|
|
||||||
class JubeatLobbyCheckHandler(JubeatBase):
|
class JubeatLobbyCheckHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_lobby_check_request(self, request: Node) -> Node:
|
def handle_lobby_check_request(self, request: Node) -> Node:
|
||||||
root = Node.void('lobby')
|
root = Node.void("lobby")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
root.add_child(data)
|
root.add_child(data)
|
||||||
|
|
||||||
data.add_child(Node.s16('interval', 0))
|
data.add_child(Node.s16("interval", 0))
|
||||||
data.add_child(Node.s16('entry_timeout', 0))
|
data.add_child(Node.s16("entry_timeout", 0))
|
||||||
entrant_nr = Node.u32('entrant_nr', 0)
|
entrant_nr = Node.u32("entrant_nr", 0)
|
||||||
entrant_nr.set_attribute('time', '0')
|
entrant_nr.set_attribute("time", "0")
|
||||||
data.add_child(entrant_nr)
|
data.add_child(entrant_nr)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
class JubeatGamendRegisterHandler(JubeatBase):
|
class JubeatGamendRegisterHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_gameend_regist_request(self, request: Node) -> Node:
|
def handle_gameend_regist_request(self, request: Node) -> Node:
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
player = data.child('player')
|
player = data.child("player")
|
||||||
|
|
||||||
if player is not None:
|
if player is not None:
|
||||||
refid = player.child_value('refid')
|
refid = player.child_value("refid")
|
||||||
else:
|
else:
|
||||||
refid = None
|
refid = None
|
||||||
|
|
||||||
@ -99,32 +94,31 @@ class JubeatGamendRegisterHandler(JubeatBase):
|
|||||||
if userid is not None and newprofile is not None:
|
if userid is not None and newprofile is not None:
|
||||||
self.put_profile(userid, newprofile)
|
self.put_profile(userid, newprofile)
|
||||||
|
|
||||||
gameend = Node.void('gameend')
|
gameend = Node.void("gameend")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
gameend.add_child(data)
|
gameend.add_child(data)
|
||||||
player = Node.void('player')
|
player = Node.void("player")
|
||||||
data.add_child(player)
|
data.add_child(player)
|
||||||
player.add_child(Node.s32('session_id', 1))
|
player.add_child(Node.s32("session_id", 1))
|
||||||
player.add_child(Node.s32('end_final_session_id', 1))
|
player.add_child(Node.s32("end_final_session_id", 1))
|
||||||
return gameend
|
return gameend
|
||||||
|
|
||||||
|
|
||||||
class JubeatGametopGetMeetingHandler(JubeatBase):
|
class JubeatGametopGetMeetingHandler(JubeatBase):
|
||||||
|
|
||||||
def handle_gametop_get_meeting_request(self, request: Node) -> Node:
|
def handle_gametop_get_meeting_request(self, request: Node) -> Node:
|
||||||
gametop = Node.void('gametop')
|
gametop = Node.void("gametop")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
gametop.add_child(data)
|
gametop.add_child(data)
|
||||||
meeting = Node.void('meeting')
|
meeting = Node.void("meeting")
|
||||||
data.add_child(meeting)
|
data.add_child(meeting)
|
||||||
single = Node.void('single')
|
single = Node.void("single")
|
||||||
meeting.add_child(single)
|
meeting.add_child(single)
|
||||||
single.set_attribute('count', '0')
|
single.set_attribute("count", "0")
|
||||||
tag = Node.void('tag')
|
tag = Node.void("tag")
|
||||||
meeting.add_child(tag)
|
meeting.add_child(tag)
|
||||||
tag.set_attribute('count', '0')
|
tag.set_attribute("count", "0")
|
||||||
reward = Node.void('reward')
|
reward = Node.void("reward")
|
||||||
data.add_child(reward)
|
data.add_child(reward)
|
||||||
reward.add_child(Node.s32('total', -1))
|
reward.add_child(Node.s32("total", -1))
|
||||||
reward.add_child(Node.s32('point', -1))
|
reward.add_child(Node.s32("point", -1))
|
||||||
return gametop
|
return gametop
|
||||||
|
@ -21,462 +21,462 @@ class JubeatCourse(JubeatBase):
|
|||||||
# List of base courses for Saucer Fulfill+ from BemaniWiki
|
# List of base courses for Saucer Fulfill+ from BemaniWiki
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'id': 1,
|
"id": 1,
|
||||||
'name': '溢れ出した記憶、特別なあなたにありがとう。',
|
"name": "溢れ出した記憶、特別なあなたにありがとう。",
|
||||||
'level': 1,
|
"level": 1,
|
||||||
'music': [
|
"music": [
|
||||||
(50000241, 2),
|
(50000241, 2),
|
||||||
(10000052, 2),
|
(10000052, 2),
|
||||||
(30000042, 2),
|
(30000042, 2),
|
||||||
(50000085, 2),
|
(50000085, 2),
|
||||||
(50000144, 2),
|
(50000144, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 2,
|
"id": 2,
|
||||||
'name': 'コースモードが怖い?ばっかお前TAGがついてるだろ',
|
"name": "コースモードが怖い?ばっかお前TAGがついてるだろ",
|
||||||
'level': 1,
|
"level": 1,
|
||||||
'music': [
|
"music": [
|
||||||
(50000121, 1),
|
(50000121, 1),
|
||||||
(30000122, 1),
|
(30000122, 1),
|
||||||
(40000159, 1),
|
(40000159, 1),
|
||||||
(50000089, 1),
|
(50000089, 1),
|
||||||
(40000051, 2),
|
(40000051, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [800000, 850000, 900000],
|
self.COURSE_REQUIREMENT_SCORE: [800000, 850000, 900000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 3,
|
"id": 3,
|
||||||
'name': '満月の鐘踊り響くは虚空から成る恋の歌',
|
"name": "満月の鐘踊り響くは虚空から成る恋の歌",
|
||||||
'level': 2,
|
"level": 2,
|
||||||
'music': [
|
"music": [
|
||||||
(40000121, 2),
|
(40000121, 2),
|
||||||
(50000188, 2),
|
(50000188, 2),
|
||||||
(30000047, 2),
|
(30000047, 2),
|
||||||
(50000237, 2),
|
(50000237, 2),
|
||||||
(50000176, 2),
|
(50000176, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 4,
|
"id": 4,
|
||||||
'name': 'スミスゼミナール 夏の陣開講記念 基本編',
|
"name": "スミスゼミナール 夏の陣開講記念 基本編",
|
||||||
'level': 2,
|
"level": 2,
|
||||||
'music': [
|
"music": [
|
||||||
(50000267, 1),
|
(50000267, 1),
|
||||||
(50000233, 1),
|
(50000233, 1),
|
||||||
(50000228, 1),
|
(50000228, 1),
|
||||||
(50000268, 1),
|
(50000268, 1),
|
||||||
(50000291, 1),
|
(50000291, 1),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 5,
|
"id": 5,
|
||||||
'name': 'HARDモードじゃないから、絶対、大丈夫だよっ!',
|
"name": "HARDモードじゃないから、絶対、大丈夫だよっ!",
|
||||||
'level': 2,
|
"level": 2,
|
||||||
'music': [
|
"music": [
|
||||||
(50000144, 2),
|
(50000144, 2),
|
||||||
(50000188, 2),
|
(50000188, 2),
|
||||||
(50000070, 2),
|
(50000070, 2),
|
||||||
(50000151, 2),
|
(50000151, 2),
|
||||||
(50000152, 2),
|
(50000152, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 1, 2],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 6,
|
"id": 6,
|
||||||
'name': '星明かりの下、愛という名の日替わりランチを君と',
|
"name": "星明かりの下、愛という名の日替わりランチを君と",
|
||||||
'level': 3,
|
"level": 3,
|
||||||
'music': [
|
"music": [
|
||||||
(50000196, 1),
|
(50000196, 1),
|
||||||
(50000151, 2),
|
(50000151, 2),
|
||||||
(50000060, 1),
|
(50000060, 1),
|
||||||
(40000048, 2),
|
(40000048, 2),
|
||||||
(10000051, 2),
|
(10000051, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 7,
|
"id": 7,
|
||||||
'name': '輝く北極星と幸せなヒーロー',
|
"name": "輝く北極星と幸せなヒーロー",
|
||||||
'level': 4,
|
"level": 4,
|
||||||
'music': [
|
"music": [
|
||||||
(50000079, 2),
|
(50000079, 2),
|
||||||
(20000044, 2),
|
(20000044, 2),
|
||||||
(50000109, 2),
|
(50000109, 2),
|
||||||
(10000043, 2),
|
(10000043, 2),
|
||||||
(10000042, 2),
|
(10000042, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 8,
|
"id": 8,
|
||||||
'name': '花-鳥-藻-夏',
|
"name": "花-鳥-藻-夏",
|
||||||
'level': 4,
|
"level": 4,
|
||||||
'music': [
|
"music": [
|
||||||
(10000068, 2),
|
(10000068, 2),
|
||||||
(40000154, 2),
|
(40000154, 2),
|
||||||
(50000123, 1),
|
(50000123, 1),
|
||||||
(40000051, 2),
|
(40000051, 2),
|
||||||
(30000045, 2),
|
(30000045, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [70, 80, 90],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 9,
|
"id": 9,
|
||||||
'name': 'TAG生誕祭2014 俺の記録を抜いてみろ!',
|
"name": "TAG生誕祭2014 俺の記録を抜いてみろ!",
|
||||||
'level': 4,
|
"level": 4,
|
||||||
'music': [
|
"music": [
|
||||||
(30000122, 2),
|
(30000122, 2),
|
||||||
(50000086, 2),
|
(50000086, 2),
|
||||||
(50000121, 2),
|
(50000121, 2),
|
||||||
(50000196, 2),
|
(50000196, 2),
|
||||||
(40000051, 2),
|
(40000051, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 967252],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 967252],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 0, 1],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [0, 0, 1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 10,
|
"id": 10,
|
||||||
'name': 'さよなら、亡くした恋と蝶の舞うヒストリア',
|
"name": "さよなら、亡くした恋と蝶の舞うヒストリア",
|
||||||
'level': 5,
|
"level": 5,
|
||||||
'music': [
|
"music": [
|
||||||
(20000041, 2),
|
(20000041, 2),
|
||||||
(30000044, 2),
|
(30000044, 2),
|
||||||
(50000037, 2),
|
(50000037, 2),
|
||||||
(20000124, 2),
|
(20000124, 2),
|
||||||
(50000033, 2),
|
(50000033, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 11,
|
"id": 11,
|
||||||
'name': 'きらきらほしふるまぼろしなぎさちゃん',
|
"name": "きらきらほしふるまぼろしなぎさちゃん",
|
||||||
'level': 5,
|
"level": 5,
|
||||||
'music': [
|
"music": [
|
||||||
(30000050, 2),
|
(30000050, 2),
|
||||||
(30000049, 2),
|
(30000049, 2),
|
||||||
(50000235, 2),
|
(50000235, 2),
|
||||||
(50000157, 2),
|
(50000157, 2),
|
||||||
(50000038, 2),
|
(50000038, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [700000, 800000, 900000],
|
self.COURSE_REQUIREMENT_SCORE: [700000, 800000, 900000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 12,
|
"id": 12,
|
||||||
'name': 'The Memorial Third: 僕みたいに演奏してね',
|
"name": "The Memorial Third: 僕みたいに演奏してね",
|
||||||
'level': 5,
|
"level": 5,
|
||||||
'music': [
|
"music": [
|
||||||
(10000037, 2),
|
(10000037, 2),
|
||||||
(20000048, 1),
|
(20000048, 1),
|
||||||
(50000253, 1),
|
(50000253, 1),
|
||||||
(20000121, 2),
|
(20000121, 2),
|
||||||
(50000133, 2),
|
(50000133, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [75, 80, 85],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [75, 80, 85],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 13,
|
"id": 13,
|
||||||
'name': 'Enjoy! 4thKAC ~ Memories of saucer ~',
|
"name": "Enjoy! 4thKAC ~ Memories of saucer ~",
|
||||||
'level': 5,
|
"level": 5,
|
||||||
'music': [
|
"music": [
|
||||||
(50000206, 1),
|
(50000206, 1),
|
||||||
(50000023, 1),
|
(50000023, 1),
|
||||||
(50000078, 1),
|
(50000078, 1),
|
||||||
(50000203, 1),
|
(50000203, 1),
|
||||||
(50000323, 1),
|
(50000323, 1),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 4],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 4],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 14,
|
"id": 14,
|
||||||
'name': '風に吹かれるキケンなシロクマダンス',
|
"name": "風に吹かれるキケンなシロクマダンス",
|
||||||
'level': 6,
|
"level": 6,
|
||||||
'music': [
|
"music": [
|
||||||
(50000059, 2),
|
(50000059, 2),
|
||||||
(50000197, 2),
|
(50000197, 2),
|
||||||
(30000037, 2),
|
(30000037, 2),
|
||||||
(50000182, 2),
|
(50000182, 2),
|
||||||
(20000038, 2),
|
(20000038, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [1, 2, 3],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 15,
|
"id": 15,
|
||||||
'name': '君主は視線で友との愛を語るめう',
|
"name": "君主は視線で友との愛を語るめう",
|
||||||
'level': 6,
|
"level": 6,
|
||||||
'music': [
|
"music": [
|
||||||
(40000052, 2),
|
(40000052, 2),
|
||||||
(50000152, 2),
|
(50000152, 2),
|
||||||
(50000090, 2),
|
(50000090, 2),
|
||||||
(20000040, 2),
|
(20000040, 2),
|
||||||
(50000184, 2),
|
(50000184, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 16,
|
"id": 16,
|
||||||
'name': 'スミスゼミナール 夏の陣開講記念 応用編',
|
"name": "スミスゼミナール 夏の陣開講記念 応用編",
|
||||||
'level': 6,
|
"level": 6,
|
||||||
'music': [
|
"music": [
|
||||||
(50000233, 2),
|
(50000233, 2),
|
||||||
(50000267, 2),
|
(50000267, 2),
|
||||||
(50000268, 2),
|
(50000268, 2),
|
||||||
(50000228, 2),
|
(50000228, 2),
|
||||||
(50000291, 2),
|
(50000291, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [750000, 850000, 900000],
|
self.COURSE_REQUIREMENT_SCORE: [750000, 850000, 900000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 17,
|
"id": 17,
|
||||||
'name': '天から降り注ぐ星はまるで甘いキャンディ',
|
"name": "天から降り注ぐ星はまるで甘いキャンディ",
|
||||||
'level': 7,
|
"level": 7,
|
||||||
'music': [
|
"music": [
|
||||||
(20000044, 2),
|
(20000044, 2),
|
||||||
(30000050, 2),
|
(30000050, 2),
|
||||||
(50000080, 2),
|
(50000080, 2),
|
||||||
(40000126, 2),
|
(40000126, 2),
|
||||||
(10000067, 2),
|
(10000067, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 18,
|
"id": 18,
|
||||||
'name': 'てんとう虫が囁いている「Wow Wow…」',
|
"name": "てんとう虫が囁いている「Wow Wow…」",
|
||||||
'level': 7,
|
"level": 7,
|
||||||
'music': [
|
"music": [
|
||||||
(50000132, 2),
|
(50000132, 2),
|
||||||
(40000128, 2),
|
(40000128, 2),
|
||||||
(10000036, 2),
|
(10000036, 2),
|
||||||
(50000119, 2),
|
(50000119, 2),
|
||||||
(50000030, 2),
|
(50000030, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [85, 90, 95],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 19,
|
"id": 19,
|
||||||
'name': 'HARDモードでも大丈夫だよ!絶対、大丈夫だよっ!',
|
"name": "HARDモードでも大丈夫だよ!絶対、大丈夫だよっ!",
|
||||||
'level': 7,
|
"level": 7,
|
||||||
'music': [
|
"music": [
|
||||||
(50000144, 2),
|
(50000144, 2),
|
||||||
(50000070, 2),
|
(50000070, 2),
|
||||||
(50000188, 2),
|
(50000188, 2),
|
||||||
(50000151, 2),
|
(50000151, 2),
|
||||||
(50000152, 2),
|
(50000152, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 20,
|
"id": 20,
|
||||||
'name': 'こんなHARDモード、滅べばいい…',
|
"name": "こんなHARDモード、滅べばいい…",
|
||||||
'level': 7,
|
"level": 7,
|
||||||
'music': [
|
"music": [
|
||||||
(50000294, 2),
|
(50000294, 2),
|
||||||
(50000295, 2),
|
(50000295, 2),
|
||||||
(50000234, 2),
|
(50000234, 2),
|
||||||
(50000245, 2),
|
(50000245, 2),
|
||||||
(50000282, 2),
|
(50000282, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
self.COURSE_REQUIREMENT_SCORE: [850000, 900000, 950000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 21,
|
"id": 21,
|
||||||
'name': 'Challenge! 4thKAC ~ Memories of saucer ~',
|
"name": "Challenge! 4thKAC ~ Memories of saucer ~",
|
||||||
'level': 7,
|
"level": 7,
|
||||||
'music': [
|
"music": [
|
||||||
(50000206, 2),
|
(50000206, 2),
|
||||||
(50000023, 2),
|
(50000023, 2),
|
||||||
(50000078, 2),
|
(50000078, 2),
|
||||||
(50000203, 2),
|
(50000203, 2),
|
||||||
(50000323, 2),
|
(50000323, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 22,
|
"id": 22,
|
||||||
'name': 'サヨナラ・キングコング ~ 恋のつぼみは愛の虹へ ~',
|
"name": "サヨナラ・キングコング ~ 恋のつぼみは愛の虹へ ~",
|
||||||
'level': 8,
|
"level": 8,
|
||||||
'music': [
|
"music": [
|
||||||
(50000148, 2),
|
(50000148, 2),
|
||||||
(50000101, 2),
|
(50000101, 2),
|
||||||
(10000064, 2),
|
(10000064, 2),
|
||||||
(50000171, 2),
|
(50000171, 2),
|
||||||
(50000070, 2),
|
(50000070, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [900000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 23,
|
"id": 23,
|
||||||
'name': '風に舞う白鳥の翼と花弁、さながら万華鏡のよう',
|
"name": "風に舞う白鳥の翼と花弁、さながら万華鏡のよう",
|
||||||
'level': 8,
|
"level": 8,
|
||||||
'music': [
|
"music": [
|
||||||
(30000036, 2),
|
(30000036, 2),
|
||||||
(50000122, 2),
|
(50000122, 2),
|
||||||
(10000062, 2),
|
(10000062, 2),
|
||||||
(50000199, 2),
|
(50000199, 2),
|
||||||
(40000153, 2),
|
(40000153, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [90, 95, 98],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [90, 95, 98],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 24,
|
"id": 24,
|
||||||
'name': 'The 小さなおぼろガチョウ♪',
|
"name": "The 小さなおぼろガチョウ♪",
|
||||||
'level': 8,
|
"level": 8,
|
||||||
'music': [
|
"music": [
|
||||||
(50000049, 2),
|
(50000049, 2),
|
||||||
(50000071, 2),
|
(50000071, 2),
|
||||||
(10000041, 2),
|
(10000041, 2),
|
||||||
(50000031, 2),
|
(50000031, 2),
|
||||||
(40000129, 2),
|
(40000129, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [970000, 980000, 990000],
|
self.COURSE_REQUIREMENT_SCORE: [970000, 980000, 990000],
|
||||||
self.COURSE_REQUIREMENT_FULL_COMBO: [2, 3, 4],
|
self.COURSE_REQUIREMENT_FULL_COMBO: [2, 3, 4],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 25,
|
"id": 25,
|
||||||
'name': 'TAG生誕祭2014 俺の記録を抜いてみろ!~ HARD編 ~',
|
"name": "TAG生誕祭2014 俺の記録を抜いてみろ!~ HARD編 ~",
|
||||||
'level': 8,
|
"level": 8,
|
||||||
'music': [
|
"music": [
|
||||||
(50000089, 2),
|
(50000089, 2),
|
||||||
(50000083, 2),
|
(50000083, 2),
|
||||||
(50000210, 2),
|
(50000210, 2),
|
||||||
(50000030, 2),
|
(50000030, 2),
|
||||||
(40000159, 2),
|
(40000159, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [800000, 900000, 931463],
|
self.COURSE_REQUIREMENT_SCORE: [800000, 900000, 931463],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 26,
|
"id": 26,
|
||||||
'name': '凍る世界で見る鳳凰の火の花',
|
"name": "凍る世界で見る鳳凰の火の花",
|
||||||
'level': 9,
|
"level": 9,
|
||||||
'music': [
|
"music": [
|
||||||
(30000043, 2),
|
(30000043, 2),
|
||||||
(10000039, 2),
|
(10000039, 2),
|
||||||
(20000048, 2),
|
(20000048, 2),
|
||||||
(50000096, 2),
|
(50000096, 2),
|
||||||
(20000038, 2),
|
(20000038, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 27,
|
"id": 27,
|
||||||
'name': '真実の桜が乱れしとき、キルト纏いし君は修羅となる',
|
"name": "真実の桜が乱れしとき、キルト纏いし君は修羅となる",
|
||||||
'level': 9,
|
"level": 9,
|
||||||
'music': [
|
"music": [
|
||||||
(50000113, 2),
|
(50000113, 2),
|
||||||
(50000184, 2),
|
(50000184, 2),
|
||||||
(50000177, 2),
|
(50000177, 2),
|
||||||
(30000124, 2),
|
(30000124, 2),
|
||||||
(50000078, 2),
|
(50000078, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
self.COURSE_REQUIREMENT_PERFECT_PERCENT: [80, 85, 90],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 28,
|
"id": 28,
|
||||||
'name': 'THE FINAL01 ~ 雷光に月、乙女に花散る祝福を ~',
|
"name": "THE FINAL01 ~ 雷光に月、乙女に花散る祝福を ~",
|
||||||
'level': 10,
|
"level": 10,
|
||||||
'music': [
|
"music": [
|
||||||
(10000038, 2),
|
(10000038, 2),
|
||||||
(20000051, 2),
|
(20000051, 2),
|
||||||
(30000048, 2),
|
(30000048, 2),
|
||||||
(40000060, 2),
|
(40000060, 2),
|
||||||
(50000023, 2),
|
(50000023, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 29,
|
"id": 29,
|
||||||
'name': 'The Memorial Third: assimilated all into Nature',
|
"name": "The Memorial Third: assimilated all into Nature",
|
||||||
'level': 10,
|
"level": 10,
|
||||||
'music': [
|
"music": [
|
||||||
(50000135, 2),
|
(50000135, 2),
|
||||||
(50000029, 2),
|
(50000029, 2),
|
||||||
(40000047, 2),
|
(40000047, 2),
|
||||||
(40000046, 2),
|
(40000046, 2),
|
||||||
(50000253, 2),
|
(50000253, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 30,
|
"id": 30,
|
||||||
'name': '4thKAC ~ Memories of saucer ~',
|
"name": "4thKAC ~ Memories of saucer ~",
|
||||||
'level': 10,
|
"level": 10,
|
||||||
'music': [
|
"music": [
|
||||||
(50000206, 2),
|
(50000206, 2),
|
||||||
(50000023, 2),
|
(50000023, 2),
|
||||||
(50000078, 2),
|
(50000078, 2),
|
||||||
(50000203, 2),
|
(50000203, 2),
|
||||||
(50000323, 2),
|
(50000323, 2),
|
||||||
],
|
],
|
||||||
'requirements': {
|
"requirements": {
|
||||||
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
self.COURSE_REQUIREMENT_SCORE: [920000, 950000, 980000],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -490,40 +490,40 @@ class JubeatCourse(JubeatBase):
|
|||||||
scores: List[int],
|
scores: List[int],
|
||||||
) -> None:
|
) -> None:
|
||||||
if len(scores) != 5:
|
if len(scores) != 5:
|
||||||
raise Exception('Invalid course scores list!')
|
raise Exception("Invalid course scores list!")
|
||||||
if rating not in [
|
if rating not in [
|
||||||
self.COURSE_RATING_FAILED,
|
self.COURSE_RATING_FAILED,
|
||||||
self.COURSE_RATING_BRONZE,
|
self.COURSE_RATING_BRONZE,
|
||||||
self.COURSE_RATING_SILVER,
|
self.COURSE_RATING_SILVER,
|
||||||
self.COURSE_RATING_GOLD,
|
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
|
# Figure out if we should update the rating/scores or not
|
||||||
oldcourse = self.data.local.game.get_achievement(
|
oldcourse = self.data.local.game.get_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
userid,
|
userid,
|
||||||
courseid,
|
courseid,
|
||||||
'course',
|
"course",
|
||||||
)
|
)
|
||||||
|
|
||||||
if oldcourse is not None:
|
if oldcourse is not None:
|
||||||
# Update the rating if the user did better
|
# 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
|
# Update the scores if the total score was better
|
||||||
if sum(scores) < sum(oldcourse.get_int_array('scores', 5)):
|
if sum(scores) < sum(oldcourse.get_int_array("scores", 5)):
|
||||||
scores = oldcourse.get_int_array('scores', 5)
|
scores = oldcourse.get_int_array("scores", 5)
|
||||||
|
|
||||||
# Save it as an achievement
|
# Save it as an achievement
|
||||||
self.data.local.game.put_achievement(
|
self.data.local.game.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
userid,
|
userid,
|
||||||
courseid,
|
courseid,
|
||||||
'course',
|
"course",
|
||||||
{
|
{
|
||||||
'rating': rating,
|
"rating": rating,
|
||||||
'scores': scores,
|
"scores": scores,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -534,9 +534,9 @@ class JubeatCourse(JubeatBase):
|
|||||||
courses = {}
|
courses = {}
|
||||||
achievements = self.data.local.game.get_achievements(self.game, userid)
|
achievements = self.data.local.game.get_achievements(self.game, userid)
|
||||||
for achievement in achievements:
|
for achievement in achievements:
|
||||||
if achievement.type == 'course':
|
if achievement.type == "course":
|
||||||
courses[achievement.id] = {
|
courses[achievement.id] = {
|
||||||
'rating': achievement.data.get_int('rating'),
|
"rating": achievement.data.get_int("rating"),
|
||||||
'scores': achievement.data.get_int_array('scores', 5),
|
"scores": achievement.data.get_int_array("scores", 5),
|
||||||
}
|
}
|
||||||
return courses
|
return courses
|
||||||
|
@ -42,29 +42,35 @@ class JubeatFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
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)
|
Base.register(gamecode, JubeatFactory)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, data: Data, config: Config, model: Model, parentmodel: Optional[Model]=None) -> Optional[Base]:
|
def create(
|
||||||
if model.gamecode == 'H44':
|
cls,
|
||||||
|
data: Data,
|
||||||
|
config: Config,
|
||||||
|
model: Model,
|
||||||
|
parentmodel: Optional[Model] = None,
|
||||||
|
) -> Optional[Base]:
|
||||||
|
if model.gamecode == "H44":
|
||||||
return Jubeat(data, config, model)
|
return Jubeat(data, config, model)
|
||||||
if model.gamecode == 'I44':
|
if model.gamecode == "I44":
|
||||||
if model.version >= 2010031800:
|
if model.version >= 2010031800:
|
||||||
return JubeatRipplesAppend(data, config, model)
|
return JubeatRipplesAppend(data, config, model)
|
||||||
else:
|
else:
|
||||||
return JubeatRipples(data, config, model)
|
return JubeatRipples(data, config, model)
|
||||||
if model.gamecode == 'J44':
|
if model.gamecode == "J44":
|
||||||
if model.version >= 2011032300:
|
if model.version >= 2011032300:
|
||||||
return JubeatKnitAppend(data, config, model)
|
return JubeatKnitAppend(data, config, model)
|
||||||
else:
|
else:
|
||||||
return JubeatKnit(data, config, model)
|
return JubeatKnit(data, config, model)
|
||||||
if model.gamecode == 'K44':
|
if model.gamecode == "K44":
|
||||||
if model.version >= 2012031400:
|
if model.version >= 2012031400:
|
||||||
return JubeatCopiousAppend(data, config, model)
|
return JubeatCopiousAppend(data, config, model)
|
||||||
else:
|
else:
|
||||||
return JubeatCopious(data, config, model)
|
return JubeatCopious(data, config, model)
|
||||||
if model.gamecode == 'L44':
|
if model.gamecode == "L44":
|
||||||
if model.version <= 2014022400:
|
if model.version <= 2014022400:
|
||||||
return JubeatSaucer(data, config, model)
|
return JubeatSaucer(data, config, model)
|
||||||
if model.version >= 2014030300 and model.version < 2015022000:
|
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):
|
class Jubeat(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat'
|
name: str = "Jubeat"
|
||||||
version: int = VersionConstants.JUBEAT
|
version: int = VersionConstants.JUBEAT
|
||||||
|
|
||||||
|
|
||||||
class JubeatRipples(JubeatBase):
|
class JubeatRipples(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Ripples'
|
name: str = "Jubeat Ripples"
|
||||||
version: int = VersionConstants.JUBEAT_RIPPLES
|
version: int = VersionConstants.JUBEAT_RIPPLES
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
@ -22,7 +22,7 @@ class JubeatRipples(JubeatBase):
|
|||||||
|
|
||||||
class JubeatRipplesAppend(JubeatBase):
|
class JubeatRipplesAppend(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Ripples Append'
|
name: str = "Jubeat Ripples Append"
|
||||||
version: int = VersionConstants.JUBEAT_RIPPLES_APPEND
|
version: int = VersionConstants.JUBEAT_RIPPLES_APPEND
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
@ -31,7 +31,7 @@ class JubeatRipplesAppend(JubeatBase):
|
|||||||
|
|
||||||
class JubeatKnit(JubeatBase):
|
class JubeatKnit(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Knit'
|
name: str = "Jubeat Knit"
|
||||||
version: int = VersionConstants.JUBEAT_KNIT
|
version: int = VersionConstants.JUBEAT_KNIT
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
@ -40,7 +40,7 @@ class JubeatKnit(JubeatBase):
|
|||||||
|
|
||||||
class JubeatKnitAppend(JubeatBase):
|
class JubeatKnitAppend(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Knit Append'
|
name: str = "Jubeat Knit Append"
|
||||||
version: int = VersionConstants.JUBEAT_KNIT_APPEND
|
version: int = VersionConstants.JUBEAT_KNIT_APPEND
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
@ -49,7 +49,7 @@ class JubeatKnitAppend(JubeatBase):
|
|||||||
|
|
||||||
class JubeatCopious(JubeatBase):
|
class JubeatCopious(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Copious'
|
name: str = "Jubeat Copious"
|
||||||
version: int = VersionConstants.JUBEAT_COPIOUS
|
version: int = VersionConstants.JUBEAT_COPIOUS
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
@ -58,7 +58,7 @@ class JubeatCopious(JubeatBase):
|
|||||||
|
|
||||||
class JubeatCopiousAppend(JubeatBase):
|
class JubeatCopiousAppend(JubeatBase):
|
||||||
|
|
||||||
name: str = 'Jubeat Copious Append'
|
name: str = "Jubeat Copious Append"
|
||||||
version: int = VersionConstants.JUBEAT_COPIOUS_APPEND
|
version: int = VersionConstants.JUBEAT_COPIOUS_APPEND
|
||||||
|
|
||||||
def previous_version(self) -> Optional[JubeatBase]:
|
def previous_version(self) -> Optional[JubeatBase]:
|
||||||
|
@ -13,7 +13,7 @@ class MetalGearArcadeBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
game: GameConstants = GameConstants.MGA
|
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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
|
@ -14,13 +14,19 @@ class MetalGearArcadeFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['I36']:
|
for gamecode in ["I36"]:
|
||||||
Base.register(gamecode, MetalGearArcadeFactory)
|
Base.register(gamecode, MetalGearArcadeFactory)
|
||||||
|
|
||||||
@classmethod
|
@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)
|
return MetalGearArcade(data, config, model)
|
||||||
|
|
||||||
# Unknown game version
|
# Unknown game version
|
||||||
|
@ -19,36 +19,36 @@ class MetalGearArcade(
|
|||||||
|
|
||||||
def __update_shop_name(self, profiledata: bytes) -> None:
|
def __update_shop_name(self, profiledata: bytes) -> None:
|
||||||
# Figure out the profile type
|
# Figure out the profile type
|
||||||
csvs = profiledata.split(b',')
|
csvs = profiledata.split(b",")
|
||||||
if len(csvs) < 2:
|
if len(csvs) < 2:
|
||||||
# Not long enough to care about
|
# Not long enough to care about
|
||||||
return
|
return
|
||||||
datatype = csvs[1].decode('ascii')
|
datatype = csvs[1].decode("ascii")
|
||||||
if datatype != 'PLAYDATA':
|
if datatype != "PLAYDATA":
|
||||||
# Not the right profile type requested
|
# Not the right profile type requested
|
||||||
return
|
return
|
||||||
|
|
||||||
# Grab the shop name
|
# Grab the shop name
|
||||||
try:
|
try:
|
||||||
shopname = csvs[30].decode('shift-jis')
|
shopname = csvs[30].decode("shift-jis")
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
self.update_machine_name(shopname)
|
self.update_machine_name(shopname)
|
||||||
|
|
||||||
def handle_system_getmaster_request(self, request: Node) -> Node:
|
def handle_system_getmaster_request(self, request: Node) -> Node:
|
||||||
# See if we can grab the request
|
# See if we can grab the request
|
||||||
data = request.child('data')
|
data = request.child("data")
|
||||||
if not data:
|
if not data:
|
||||||
root = Node.void('system')
|
root = Node.void("system")
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Figure out what type of messsage this is
|
# Figure out what type of messsage this is
|
||||||
reqtype = data.child_value('datatype')
|
reqtype = data.child_value("datatype")
|
||||||
reqkey = data.child_value('datakey')
|
reqkey = data.child_value("datakey")
|
||||||
|
|
||||||
# System message
|
# System message
|
||||||
root = Node.void('system')
|
root = Node.void("system")
|
||||||
|
|
||||||
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
if reqtype == "S_SRVMSG" and reqkey == "INFO":
|
||||||
# Generate system message
|
# 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"
|
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.
|
# 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(
|
||||||
root.add_child(Node.string('strdata2', base64.b64encode(settings2_str.encode('ascii')).decode('ascii')))
|
Node.string(
|
||||||
root.add_child(Node.u64('updatedate', Time.now() * 1000))
|
"strdata1",
|
||||||
root.add_child(Node.s32('result', 1))
|
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:
|
else:
|
||||||
# Unknown message.
|
# Unknown message.
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||||
# Look up user by refid
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
root.add_child(
|
||||||
|
Node.s32("result", 1)
|
||||||
|
) # Unclear if this is the right thing to do here.
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Extract new profile info from old profile
|
# Extract new profile info from old profile
|
||||||
@ -87,14 +99,14 @@ class MetalGearArcade(
|
|||||||
self.put_profile(userid, newprofile)
|
self.put_profile(userid, newprofile)
|
||||||
|
|
||||||
# Return success!
|
# Return success!
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 0))
|
root.add_child(Node.s32("result", 0))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||||
# Look up user by refid
|
# Look up user by refid
|
||||||
refid = request.child_value('data/eaid')
|
refid = request.child_value("data/eaid")
|
||||||
profiletypes = request.child_value('data/recv_csv').split(',')
|
profiletypes = request.child_value("data/recv_csv").split(",")
|
||||||
profile = None
|
profile = None
|
||||||
userid = None
|
userid = None
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
@ -104,58 +116,66 @@ class MetalGearArcade(
|
|||||||
if profile is not None:
|
if profile is not None:
|
||||||
return self.format_profile(userid, profiletypes, profile)
|
return self.format_profile(userid, profiletypes, profile)
|
||||||
else:
|
else:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s32('result', 1)) # Unclear if this is the right thing to do here.
|
root.add_child(
|
||||||
|
Node.s32("result", 1)
|
||||||
|
) # Unclear if this is the right thing to do here.
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profiletypes: List[str], profile: Profile) -> Node:
|
def format_profile(
|
||||||
root = Node.void('playerdata')
|
self, userid: UserID, profiletypes: List[str], profile: Profile
|
||||||
root.add_child(Node.s32('result', 0))
|
) -> Node:
|
||||||
player = Node.void('player')
|
root = Node.void("playerdata")
|
||||||
|
root.add_child(Node.s32("result", 0))
|
||||||
|
player = Node.void("player")
|
||||||
root.add_child(player)
|
root.add_child(player)
|
||||||
records = 0
|
records = 0
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
player.add_child(record)
|
player.add_child(record)
|
||||||
|
|
||||||
for profiletype in profiletypes:
|
for profiletype in profiletypes:
|
||||||
if profiletype == "3fffffffff":
|
if profiletype == "3fffffffff":
|
||||||
continue
|
continue
|
||||||
for j in range(len(profile['strdatas'])):
|
for j in range(len(profile["strdatas"])):
|
||||||
strdata = profile['strdatas'][j]
|
strdata = profile["strdatas"][j]
|
||||||
bindata = profile['bindatas'][j]
|
bindata = profile["bindatas"][j]
|
||||||
|
|
||||||
# Figure out the profile type
|
# Figure out the profile type
|
||||||
csvs = strdata.split(b',')
|
csvs = strdata.split(b",")
|
||||||
if len(csvs) < 2:
|
if len(csvs) < 2:
|
||||||
# Not long enough to care about
|
# Not long enough to care about
|
||||||
continue
|
continue
|
||||||
datatype = csvs[1].decode('ascii')
|
datatype = csvs[1].decode("ascii")
|
||||||
if datatype != profiletype:
|
if datatype != profiletype:
|
||||||
# Not the right profile type requested
|
# Not the right profile type requested
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# This is a valid profile node for this type, lets return only the profile values
|
# This is a valid profile node for this type, lets return only the profile values
|
||||||
strdata = b','.join(csvs[2:])
|
strdata = b",".join(csvs[2:])
|
||||||
d = Node.string('d', base64.b64encode(strdata).decode('ascii'))
|
d = Node.string("d", base64.b64encode(strdata).decode("ascii"))
|
||||||
record.add_child(d)
|
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
|
# Remember that we had this record
|
||||||
records = records + 1
|
records = records + 1
|
||||||
|
|
||||||
player.add_child(Node.u32('record_num', records))
|
player.add_child(Node.u32("record_num", records))
|
||||||
return root
|
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.
|
# Profile save request, data values are base64 encoded.
|
||||||
# d is a CSV, and bin1 is binary data.
|
# d is a CSV, and bin1 is binary data.
|
||||||
newprofile = oldprofile.clone()
|
newprofile = oldprofile.clone()
|
||||||
strdatas: List[bytes] = []
|
strdatas: List[bytes] = []
|
||||||
bindatas: List[bytes] = []
|
bindatas: List[bytes] = []
|
||||||
|
|
||||||
record = request.child('data/record')
|
record = request.child("data/record")
|
||||||
for node in record.children:
|
for node in record.children:
|
||||||
if node.name != 'd':
|
if node.name != "d":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
profile = base64.b64decode(node.value)
|
profile = base64.b64decode(node.value)
|
||||||
@ -165,10 +185,10 @@ class MetalGearArcade(
|
|||||||
if is_new:
|
if is_new:
|
||||||
self.__update_shop_name(profile)
|
self.__update_shop_name(profile)
|
||||||
strdatas.append(profile)
|
strdatas.append(profile)
|
||||||
bindatas.append(base64.b64decode(node.child_value('bin1')))
|
bindatas.append(base64.b64decode(node.child_value("bin1")))
|
||||||
|
|
||||||
newprofile['strdatas'] = strdatas
|
newprofile["strdatas"] = strdatas
|
||||||
newprofile['bindatas'] = bindatas
|
newprofile["bindatas"] = bindatas
|
||||||
|
|
||||||
# Keep track of play statistics across all versions
|
# Keep track of play statistics across all versions
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
|||||||
|
|
||||||
from bemani.backend.base import Base
|
from bemani.backend.base import Base
|
||||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
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.data import UserID, Config, Data
|
||||||
from bemani.protocol import Node
|
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:
|
def __init__(self, data: Data, config: Config, model: Model) -> None:
|
||||||
super().__init__(data, config, model)
|
super().__init__(data, config, model)
|
||||||
if model.rev == 'X':
|
if model.rev == "X":
|
||||||
self.omnimix = True
|
self.omnimix = True
|
||||||
else:
|
else:
|
||||||
self.omnimix = False
|
self.omnimix = False
|
||||||
@ -47,7 +54,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
return DBConstants.OMNIMIX_VERSION_BUMP + self.version
|
||||||
return 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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -56,19 +63,19 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
|
|
||||||
def game_to_db_clear_type(self, clear_type: int) -> int:
|
def game_to_db_clear_type(self, clear_type: int) -> int:
|
||||||
# Given a game clear type, return the canonical database identifier.
|
# 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:
|
def db_to_game_clear_type(self, clear_type: int) -> int:
|
||||||
# Given a database clear type, return the game's identifier.
|
# 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:
|
def game_to_db_grade(self, grade: int) -> int:
|
||||||
# Given a game grade, return the canonical database identifier.
|
# 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:
|
def db_to_game_grade(self, grade: int) -> int:
|
||||||
# Given a database grade, return the game's identifier.
|
# 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]:
|
def get_profile_by_refid(self, refid: Optional[str]) -> Optional[Node]:
|
||||||
"""
|
"""
|
||||||
@ -88,7 +95,9 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
# Now, return it
|
# Now, return it
|
||||||
return self.format_profile(userid, profile)
|
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
|
Given a RefID and an optional name, create a profile and then return
|
||||||
a formatted profile node. Similar rationale to get_profile_by_refid.
|
a formatted profile node. Similar rationale to get_profile_by_refid.
|
||||||
@ -97,7 +106,7 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'NONAME'
|
name = "NONAME"
|
||||||
|
|
||||||
# First, create and save the default profile
|
# First, create and save the default profile
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -107,8 +116,8 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
'loc': locid,
|
"loc": locid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.put_profile(userid, profile)
|
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,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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.
|
return a new profile that's been updated with the contents of the request.
|
||||||
@ -142,7 +153,8 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
all_attempts, remote_attempts = Parallel.execute([
|
all_attempts, remote_attempts = Parallel.execute(
|
||||||
|
[
|
||||||
lambda: self.data.local.music.get_all_attempts(
|
lambda: self.data.local.music.get_all_attempts(
|
||||||
game=self.game,
|
game=self.game,
|
||||||
version=self.music_version,
|
version=self.music_version,
|
||||||
@ -150,8 +162,9 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
lambda: self.data.remote.music.get_clear_rates(
|
lambda: self.data.remote.music.get_clear_rates(
|
||||||
game=self.game,
|
game=self.game,
|
||||||
version=self.music_version,
|
version=self.music_version,
|
||||||
|
),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
])
|
|
||||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||||
for (_, attempt) in all_attempts:
|
for (_, attempt) in all_attempts:
|
||||||
# Terrible temporary structure is terrible.
|
# Terrible temporary structure is terrible.
|
||||||
@ -159,19 +172,26 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
attempts[attempt.id] = {}
|
attempts[attempt.id] = {}
|
||||||
if attempt.chart not in attempts[attempt.id]:
|
if attempt.chart not in attempts[attempt.id]:
|
||||||
attempts[attempt.id][attempt.chart] = {
|
attempts[attempt.id][attempt.chart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
# We saw an attempt, keep the total attempts in sync.
|
# 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
|
# This attempt was a failure, so don't count it against clears of full combos
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# It was at least a clear
|
# 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
|
# Merge in remote attempts
|
||||||
for songid in remote_attempts:
|
for songid in remote_attempts:
|
||||||
@ -181,12 +201,16 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
for songchart in remote_attempts[songid]:
|
for songchart in remote_attempts[songid]:
|
||||||
if songchart not in attempts[songid]:
|
if songchart not in attempts[songid]:
|
||||||
attempts[songid][songchart] = {
|
attempts[songid][songchart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
songchart
|
||||||
|
]["plays"]
|
||||||
|
attempts[songid][songchart]["clears"] += remote_attempts[songid][
|
||||||
|
songchart
|
||||||
|
]["clears"]
|
||||||
|
|
||||||
return attempts
|
return attempts
|
||||||
|
|
||||||
@ -256,21 +280,23 @@ class MusecaBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Replace grade and clear type
|
# Replace grade and clear type
|
||||||
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
|
scoredata.replace_int(
|
||||||
history.replace_int('clear_type', clear_type)
|
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||||
scoredata.replace_int('grade', max(scoredata.get_int('grade'), grade))
|
)
|
||||||
history.replace_int('grade', grade)
|
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
|
# If we have a combo, replace it
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
|
|
||||||
# If we have play stats, replace it
|
# If we have play stats, replace it
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
if raised:
|
if raised:
|
||||||
# We have stats, and there's a new high score, update the stats
|
# We have stats, and there's a new high score, update the stats
|
||||||
scoredata.replace_dict('stats', stats)
|
scoredata.replace_dict("stats", stats)
|
||||||
history.replace_dict('stats', stats)
|
history.replace_dict("stats", stats)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
|
@ -4,138 +4,136 @@ from bemani.protocol import Node
|
|||||||
|
|
||||||
|
|
||||||
class MusecaGameShopHandler(MusecaBase):
|
class MusecaGameShopHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_shop_request(self, request: Node) -> Node:
|
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
|
# Respond with number of milliseconds until next request
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class MusecaGameHiscoreHandler(MusecaBase):
|
class MusecaGameHiscoreHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_hiscore_request(self, request: Node) -> Node:
|
def handle_game_3_hiscore_request(self, request: Node) -> Node:
|
||||||
# Grab location for local scores
|
# 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
|
# Start the response packet
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
|
|
||||||
# First, grab hit chart
|
# First, grab hit chart
|
||||||
playcounts = self.data.local.music.get_hit_chart(self.game, self.version, 1024)
|
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)
|
game.add_child(hitchart)
|
||||||
for (songid, count) in playcounts:
|
for (songid, count) in playcounts:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
hitchart.add_child(info)
|
hitchart.add_child(info)
|
||||||
info.add_child(Node.u32('id', songid))
|
info.add_child(Node.u32("id", songid))
|
||||||
info.add_child(Node.u32('cnt', count))
|
info.add_child(Node.u32("cnt", count))
|
||||||
|
|
||||||
# Now, grab user records
|
# Now, grab user records
|
||||||
records = self.data.remote.music.get_all_records(self.game, self.version)
|
records = self.data.remote.music.get_all_records(self.game, self.version)
|
||||||
users = {
|
users = {
|
||||||
uid: prof for (uid, prof) in
|
uid: prof for (uid, prof) in self.get_any_profiles([r[0] for r in records])
|
||||||
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)
|
game.add_child(hiscore_allover)
|
||||||
|
|
||||||
# Output records
|
# Output records
|
||||||
for (userid, score) in records:
|
for (userid, score) in records:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
|
|
||||||
if userid not in users:
|
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]
|
profile = users[userid]
|
||||||
|
|
||||||
info.add_child(Node.u32('id', score.id))
|
info.add_child(Node.u32("id", score.id))
|
||||||
info.add_child(Node.u32('type', score.chart))
|
info.add_child(Node.u32("type", score.chart))
|
||||||
info.add_child(Node.string('name', profile.get_str('name')))
|
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.string("seq", ID.format_extid(profile.extid)))
|
||||||
info.add_child(Node.u32('score', score.points))
|
info.add_child(Node.u32("score", score.points))
|
||||||
|
|
||||||
# Add to global scores
|
# Add to global scores
|
||||||
hiscore_allover.add_child(info)
|
hiscore_allover.add_child(info)
|
||||||
|
|
||||||
# Now, grab local records
|
# Now, grab local records
|
||||||
area_users = [
|
area_users = [
|
||||||
uid for (uid, prof) in self.data.local.user.get_all_profiles(self.game, self.version)
|
uid
|
||||||
if prof.get_int('loc', -1) == locid
|
for (uid, prof) in self.data.local.user.get_all_profiles(
|
||||||
]
|
self.game, self.version
|
||||||
records = self.data.local.music.get_all_records(self.game, self.version, userlist=area_users)
|
)
|
||||||
missing_players = [
|
if prof.get_int("loc", -1) == locid
|
||||||
uid for (uid, _) in records
|
|
||||||
if uid not in users
|
|
||||||
]
|
]
|
||||||
|
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):
|
for (uid, prof) in self.get_any_profiles(missing_players):
|
||||||
users[uid] = prof
|
users[uid] = prof
|
||||||
|
|
||||||
hiscore_location = Node.void('hiscore_location')
|
hiscore_location = Node.void("hiscore_location")
|
||||||
game.add_child(hiscore_location)
|
game.add_child(hiscore_location)
|
||||||
|
|
||||||
# Output records
|
# Output records
|
||||||
for (userid, score) in records:
|
for (userid, score) in records:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
|
|
||||||
if userid not in users:
|
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]
|
profile = users[userid]
|
||||||
|
|
||||||
info.add_child(Node.u32('id', score.id))
|
info.add_child(Node.u32("id", score.id))
|
||||||
info.add_child(Node.u32('type', score.chart))
|
info.add_child(Node.u32("type", score.chart))
|
||||||
info.add_child(Node.string('name', profile.get_str('name')))
|
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.string("seq", ID.format_extid(profile.extid)))
|
||||||
info.add_child(Node.u32('score', score.points))
|
info.add_child(Node.u32("score", score.points))
|
||||||
|
|
||||||
# Add to global scores
|
# Add to global scores
|
||||||
hiscore_location.add_child(info)
|
hiscore_location.add_child(info)
|
||||||
|
|
||||||
# Now, grab clear rates
|
# Now, grab clear rates
|
||||||
clear_rate = Node.void('clear_rate')
|
clear_rate = Node.void("clear_rate")
|
||||||
game.add_child(clear_rate)
|
game.add_child(clear_rate)
|
||||||
|
|
||||||
clears = self.get_clear_rates()
|
clears = self.get_clear_rates()
|
||||||
for songid in clears:
|
for songid in clears:
|
||||||
for chart in clears[songid]:
|
for chart in clears[songid]:
|
||||||
if clears[songid][chart]['total'] > 0:
|
if clears[songid][chart]["total"] > 0:
|
||||||
rate = float(clears[songid][chart]['clears']) / float(clears[songid][chart]['total'])
|
rate = float(clears[songid][chart]["clears"]) / float(
|
||||||
dnode = Node.void('d')
|
clears[songid][chart]["total"]
|
||||||
|
)
|
||||||
|
dnode = Node.void("d")
|
||||||
clear_rate.add_child(dnode)
|
clear_rate.add_child(dnode)
|
||||||
dnode.add_child(Node.u32('id', songid))
|
dnode.add_child(Node.u32("id", songid))
|
||||||
dnode.add_child(Node.u32('type', chart))
|
dnode.add_child(Node.u32("type", chart))
|
||||||
dnode.add_child(Node.s16('cr', int(rate * 10000)))
|
dnode.add_child(Node.s16("cr", int(rate * 10000)))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class MusecaGameFrozenHandler(MusecaBase):
|
class MusecaGameFrozenHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_frozen_request(self, request: Node) -> Node:
|
def handle_game_3_frozen_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
game.add_child(Node.u8('result', 0))
|
game.add_child(Node.u8("result", 0))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class MusecaGameNewHandler(MusecaBase):
|
class MusecaGameNewHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_new_request(self, request: Node) -> Node:
|
def handle_game_3_new_request(self, request: Node) -> Node:
|
||||||
refid = request.child_value('refid')
|
refid = request.child_value("refid")
|
||||||
name = request.child_value('name')
|
name = request.child_value("name")
|
||||||
loc = ID.parse_machine_id(request.child_value('locid'))
|
loc = ID.parse_machine_id(request.child_value("locid"))
|
||||||
self.new_profile_by_refid(refid, name, loc)
|
self.new_profile_by_refid(refid, name, loc)
|
||||||
|
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
class MusecaGameSaveMusicHandler(MusecaBase):
|
class MusecaGameSaveMusicHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_save_m_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -143,19 +141,19 @@ class MusecaGameSaveMusicHandler(MusecaBase):
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
# Doesn't matter if userid is None here, that's an anonymous score
|
# Doesn't matter if userid is None here, that's an anonymous score
|
||||||
musicid = request.child_value('music_id')
|
musicid = request.child_value("music_id")
|
||||||
chart = request.child_value('music_type')
|
chart = request.child_value("music_type")
|
||||||
points = request.child_value('score')
|
points = request.child_value("score")
|
||||||
combo = request.child_value('max_chain')
|
combo = request.child_value("max_chain")
|
||||||
clear_type = self.game_to_db_clear_type(request.child_value('clear_type'))
|
clear_type = self.game_to_db_clear_type(request.child_value("clear_type"))
|
||||||
grade = self.game_to_db_grade(request.child_value('score_grade'))
|
grade = self.game_to_db_grade(request.child_value("score_grade"))
|
||||||
stats = {
|
stats = {
|
||||||
'btn_rate': request.child_value('btn_rate'),
|
"btn_rate": request.child_value("btn_rate"),
|
||||||
'long_rate': request.child_value('long_rate'),
|
"long_rate": request.child_value("long_rate"),
|
||||||
'vol_rate': request.child_value('vol_rate'),
|
"vol_rate": request.child_value("vol_rate"),
|
||||||
'critical': request.child_value('critical'),
|
"critical": request.child_value("critical"),
|
||||||
'near': request.child_value('near'),
|
"near": request.child_value("near"),
|
||||||
'error': request.child_value('error'),
|
"error": request.child_value("error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Save the score
|
# Save the score
|
||||||
@ -171,19 +169,17 @@ class MusecaGameSaveMusicHandler(MusecaBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Return a blank response
|
# Return a blank response
|
||||||
return Node.void('game_3')
|
return Node.void("game_3")
|
||||||
|
|
||||||
|
|
||||||
class MusecaGamePlayEndHandler(MusecaBase):
|
class MusecaGamePlayEndHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_play_e_request(self, request: Node) -> Node:
|
def handle_game_3_play_e_request(self, request: Node) -> Node:
|
||||||
return Node.void('game_3')
|
return Node.void("game_3")
|
||||||
|
|
||||||
|
|
||||||
class MusecaGameSaveHandler(MusecaBase):
|
class MusecaGameSaveHandler(MusecaBase):
|
||||||
|
|
||||||
def handle_game_3_save_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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:
|
if userid is not None and newprofile is not None:
|
||||||
self.put_profile(userid, newprofile)
|
self.put_profile(userid, newprofile)
|
||||||
|
|
||||||
return Node.void('game_3')
|
return Node.void("game_3")
|
||||||
|
@ -16,12 +16,17 @@ class MusecaFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['PIX']:
|
for gamecode in ["PIX"]:
|
||||||
Base.register(gamecode, MusecaFactory)
|
Base.register(gamecode, MusecaFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date <= 2016072600:
|
if date <= 2016072600:
|
||||||
return VersionConstants.MUSECA
|
return VersionConstants.MUSECA
|
||||||
@ -29,7 +34,7 @@ class MusecaFactory(Factory):
|
|||||||
return VersionConstants.MUSECA_1_PLUS
|
return VersionConstants.MUSECA_1_PLUS
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'PIX':
|
if model.gamecode == "PIX":
|
||||||
version = version_from_date(model.version)
|
version = version_from_date(model.version)
|
||||||
if version == VersionConstants.MUSECA:
|
if version == VersionConstants.MUSECA:
|
||||||
return Museca1(data, config, model)
|
return Museca1(data, config, model)
|
||||||
|
@ -59,12 +59,12 @@ class Museca1(
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -109,34 +109,37 @@ class Museca1(
|
|||||||
}[grade]
|
}[grade]
|
||||||
|
|
||||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
limited = Node.void('music_limited')
|
limited = Node.void("music_limited")
|
||||||
game.add_child(limited)
|
game.add_child(limited)
|
||||||
|
|
||||||
# Song unlock config
|
# Song unlock config
|
||||||
game_config = self.get_game_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()
|
ids = set()
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||||
for song in songs:
|
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))
|
ids.add((song.id, song.chart))
|
||||||
|
|
||||||
for (songid, chart) in ids:
|
for (songid, chart) in ids:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
limited.add_child(info)
|
limited.add_child(info)
|
||||||
info.add_child(Node.s32('music_id', songid))
|
info.add_child(Node.s32("music_id", songid))
|
||||||
info.add_child(Node.u8('music_type', chart))
|
info.add_child(Node.u8("music_type", chart))
|
||||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||||
|
|
||||||
# Event config
|
# Event config
|
||||||
event = Node.void('event')
|
event = Node.void("event")
|
||||||
game.add_child(event)
|
game.add_child(event)
|
||||||
|
|
||||||
def enable_event(eid: int) -> None:
|
def enable_event(eid: int) -> None:
|
||||||
evt = Node.void('info')
|
evt = Node.void("info")
|
||||||
event.add_child(evt)
|
event.add_child(evt)
|
||||||
evt.add_child(Node.u32('event_id', eid))
|
evt.add_child(Node.u32("event_id", eid))
|
||||||
|
|
||||||
# Allow PASELI light start
|
# Allow PASELI light start
|
||||||
enable_event(83)
|
enable_event(83)
|
||||||
@ -153,21 +156,21 @@ class Museca1(
|
|||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
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:
|
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)
|
root = self.get_profile_by_refid(refid)
|
||||||
if root is not None:
|
if root is not None:
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# No data succession, there's nothing older than this!
|
# No data succession, there's nothing older than this!
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
root.add_child(Node.u8('result', 1))
|
root.add_child(Node.u8("result", 1))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_3_load_m_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -175,154 +178,186 @@ class Museca1(
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
if userid is not 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:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
# Output to the game
|
# Output to the game
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
new = Node.void('new')
|
new = Node.void("new")
|
||||||
game.add_child(new)
|
game.add_child(new)
|
||||||
|
|
||||||
for score in scores:
|
for score in scores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
new.add_child(music)
|
new.add_child(music)
|
||||||
music.add_child(Node.u32('music_id', score.id))
|
music.add_child(Node.u32("music_id", score.id))
|
||||||
music.add_child(Node.u32('music_type', score.chart))
|
music.add_child(Node.u32("music_type", score.chart))
|
||||||
music.add_child(Node.u32('score', score.points))
|
music.add_child(Node.u32("score", score.points))
|
||||||
music.add_child(Node.u32('cnt', score.plays))
|
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(
|
||||||
music.add_child(Node.u32('score_grade', self.db_to_game_grade(score.data.get_int('grade'))))
|
Node.u32(
|
||||||
stats = score.data.get_dict('stats')
|
"clear_type",
|
||||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
self.db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||||
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(
|
||||||
|
"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
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
|
|
||||||
# Generic profile stuff
|
# Generic profile stuff
|
||||||
game.add_child(Node.string('name', profile.get_str('name')))
|
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.string("code", ID.format_extid(profile.extid)))
|
||||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
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("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.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(
|
||||||
game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy')))
|
Node.s32_array("hidden_param", profile.get_int_array("hidden_param", 20))
|
||||||
game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count')))
|
)
|
||||||
|
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
|
# Play statistics
|
||||||
statistics = self.get_play_statistics(userid)
|
statistics = self.get_play_statistics(userid)
|
||||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
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("daily_count", statistics.today_plays))
|
||||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
game.add_child(Node.u32("play_chain", statistics.consecutive_days))
|
||||||
|
|
||||||
# Last played stuff
|
# Last played stuff
|
||||||
if 'last' in profile:
|
if "last" in profile:
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
game.add_child(last)
|
game.add_child(last)
|
||||||
last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1)))
|
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("music_type", lastdict.get_int("music_type")))
|
||||||
last.add_child(Node.u8('sort_type', lastdict.get_int('sort_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("narrow_down", lastdict.get_int("narrow_down")))
|
||||||
last.add_child(Node.u8('headphone', lastdict.get_int('headphone')))
|
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("appeal_id", lastdict.get_int("appeal_id", 1001)))
|
||||||
last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id')))
|
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.u8("gauge_option", lastdict.get_int("gauge_option")))
|
||||||
|
|
||||||
# Item unlocks
|
# Item unlocks
|
||||||
itemnode = Node.void('item')
|
itemnode = Node.void("item")
|
||||||
game.add_child(itemnode)
|
game.add_child(itemnode)
|
||||||
|
|
||||||
game_config = self.get_game_config()
|
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:
|
for item in achievements:
|
||||||
if item.type[:5] != 'item_':
|
if item.type[:5] != "item_":
|
||||||
continue
|
continue
|
||||||
itemtype = int(item.type[5:])
|
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
|
# Don't echo unlocked songs, we will add all of them later
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
itemnode.add_child(info)
|
itemnode.add_child(info)
|
||||||
info.add_child(Node.u8('type', itemtype))
|
info.add_child(Node.u8("type", itemtype))
|
||||||
info.add_child(Node.u32('id', item.id))
|
info.add_child(Node.u32("id", item.id))
|
||||||
info.add_child(Node.u32('param', item.data.get_int('param')))
|
info.add_child(Node.u32("param", item.data.get_int("param")))
|
||||||
if 'diff_param' in item.data:
|
if "diff_param" in item.data:
|
||||||
info.add_child(Node.s32('diff_param', item.data.get_int('diff_param')))
|
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] = {}
|
ids: Dict[int, int] = {}
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||||
for song in songs:
|
for song in songs:
|
||||||
if song.id not in ids:
|
if song.id not in ids:
|
||||||
ids[song.id] = 0
|
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)
|
ids[song.id] = ids[song.id] | (1 << song.chart)
|
||||||
|
|
||||||
for itemid in ids:
|
for itemid in ids:
|
||||||
if ids[itemid] == 0:
|
if ids[itemid] == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
itemnode.add_child(info)
|
itemnode.add_child(info)
|
||||||
info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG))
|
info.add_child(Node.u8("type", self.GAME_CATALOG_TYPE_SONG))
|
||||||
info.add_child(Node.u32('id', itemid))
|
info.add_child(Node.u32("id", itemid))
|
||||||
info.add_child(Node.u32('param', ids[itemid]))
|
info.add_child(Node.u32("param", ids[itemid]))
|
||||||
|
|
||||||
return game
|
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()
|
newprofile = oldprofile.clone()
|
||||||
|
|
||||||
# Update blaster energy and in-game currencies
|
# 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:
|
if earned_gamecoin_packet is not None:
|
||||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
newprofile.replace_int(
|
||||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||||
|
)
|
||||||
|
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||||
if earned_gamecoin_block is not None:
|
if earned_gamecoin_block is not None:
|
||||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
newprofile.replace_int(
|
||||||
earned_blaster_energy = request.child_value('earned_blaster_energy')
|
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||||
|
)
|
||||||
|
earned_blaster_energy = request.child_value("earned_blaster_energy")
|
||||||
if earned_blaster_energy is not None:
|
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
|
# Miscelaneous stuff
|
||||||
newprofile.replace_int('blaster_count', request.child_value('blaster_count'))
|
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("skill_name_id", request.child_value("skill_name_id"))
|
||||||
newprofile.replace_int_array('hidden_param', 20, request.child_value('hidden_param'))
|
newprofile.replace_int_array(
|
||||||
|
"hidden_param", 20, request.child_value("hidden_param")
|
||||||
|
)
|
||||||
|
|
||||||
# Update user's unlock status if we aren't force unlocked
|
# Update user's unlock status if we aren't force unlocked
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
|
|
||||||
if request.child('item') is not None:
|
if request.child("item") is not None:
|
||||||
for child in request.child('item').children:
|
for child in request.child("item").children:
|
||||||
if child.name != 'info':
|
if child.name != "info":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_id = child.child_value('id')
|
item_id = child.child_value("id")
|
||||||
item_type = child.child_value('type')
|
item_type = child.child_value("type")
|
||||||
param = child.child_value('param')
|
param = child.child_value("param")
|
||||||
diff_param = child.child_value('diff_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
|
# Don't save back songs, because they were force unlocked
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if diff_param is not None:
|
if diff_param is not None:
|
||||||
paramvals = {
|
paramvals = {
|
||||||
'diff_param': diff_param,
|
"diff_param": diff_param,
|
||||||
'param': param,
|
"param": param,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
paramvals = {
|
paramvals = {
|
||||||
'param': param,
|
"param": param,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
@ -330,23 +365,23 @@ class Museca1(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
item_id,
|
item_id,
|
||||||
f'item_{item_type}',
|
f"item_{item_type}",
|
||||||
paramvals,
|
paramvals,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Grab last information.
|
# Grab last information.
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||||
lastdict.replace_int('comment_id', request.child_value('comment_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_id", request.child_value("music_id"))
|
||||||
lastdict.replace_int('music_type', request.child_value('music_type'))
|
lastdict.replace_int("music_type", request.child_value("music_type"))
|
||||||
lastdict.replace_int('sort_type', request.child_value('sort_type'))
|
lastdict.replace_int("sort_type", request.child_value("sort_type"))
|
||||||
lastdict.replace_int('narrow_down', request.child_value('narrow_down'))
|
lastdict.replace_int("narrow_down", request.child_value("narrow_down"))
|
||||||
lastdict.replace_int('gauge_option', request.child_value('gauge_option'))
|
lastdict.replace_int("gauge_option", request.child_value("gauge_option"))
|
||||||
|
|
||||||
# Save back last information gleaned from results
|
# Save back last information gleaned from results
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Keep track of play statistics
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
@ -64,18 +64,18 @@ class Museca1Plus(
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Disable Online Matching',
|
"name": "Disable Online Matching",
|
||||||
'tip': 'Disable online matching between games.',
|
"tip": "Disable online matching between games.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'disable_matching',
|
"setting": "disable_matching",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -121,36 +121,39 @@ class Museca1Plus(
|
|||||||
}[grade]
|
}[grade]
|
||||||
|
|
||||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
limited = Node.void('music_limited')
|
limited = Node.void("music_limited")
|
||||||
game.add_child(limited)
|
game.add_child(limited)
|
||||||
|
|
||||||
# Song unlock config
|
# Song unlock config
|
||||||
game_config = self.get_game_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()
|
ids = set()
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||||
for song in songs:
|
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))
|
ids.add((song.id, song.chart))
|
||||||
|
|
||||||
for (songid, chart) in ids:
|
for (songid, chart) in ids:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
limited.add_child(info)
|
limited.add_child(info)
|
||||||
info.add_child(Node.s32('music_id', songid))
|
info.add_child(Node.s32("music_id", songid))
|
||||||
info.add_child(Node.u8('music_type', chart))
|
info.add_child(Node.u8("music_type", chart))
|
||||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||||
|
|
||||||
# Event config
|
# Event config
|
||||||
event = Node.void('event')
|
event = Node.void("event")
|
||||||
game.add_child(event)
|
game.add_child(event)
|
||||||
|
|
||||||
def enable_event(eid: int) -> None:
|
def enable_event(eid: int) -> None:
|
||||||
evt = Node.void('info')
|
evt = Node.void("info")
|
||||||
event.add_child(evt)
|
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
|
enable_event(143) # Matching enabled
|
||||||
|
|
||||||
# These events are meant specifically for Museca Plus
|
# These events are meant specifically for Museca Plus
|
||||||
@ -227,52 +230,52 @@ class Museca1Plus(
|
|||||||
# 149, # MUKIPARA UNLOCKS
|
# 149, # MUKIPARA UNLOCKS
|
||||||
|
|
||||||
# Makes special missions available on grafica that have them.
|
# Makes special missions available on grafica that have them.
|
||||||
extend = Node.void('extend')
|
extend = Node.void("extend")
|
||||||
game.add_child(extend)
|
game.add_child(extend)
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
extend.add_child(info)
|
extend.add_child(info)
|
||||||
info.add_child(Node.u32('extend_id', 1))
|
info.add_child(Node.u32("extend_id", 1))
|
||||||
info.add_child(Node.u32('extend_type', 9))
|
info.add_child(Node.u32("extend_type", 9))
|
||||||
info.add_child(Node.s32('param_num_1', 2))
|
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_2", 50))
|
||||||
info.add_child(Node.s32('param_num_3', 59))
|
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_4", 64))
|
||||||
info.add_child(Node.s32('param_num_5', 86))
|
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_1", "available_ex: 1"))
|
||||||
info.add_child(Node.string('param_str_2', '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_3", "available_ex: 1"))
|
||||||
info.add_child(Node.string('param_str_4', '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.string("param_str_5", "available_ex: 1"))
|
||||||
|
|
||||||
if self.omnimix:
|
if self.omnimix:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
extend.add_child(info)
|
extend.add_child(info)
|
||||||
info.add_child(Node.u32('extend_id', 2))
|
info.add_child(Node.u32("extend_id", 2))
|
||||||
info.add_child(Node.u32('extend_type', 9))
|
info.add_child(Node.u32("extend_type", 9))
|
||||||
info.add_child(Node.s32('param_num_1', 210))
|
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_2", 0))
|
||||||
info.add_child(Node.s32('param_num_3', 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_4", 0))
|
||||||
info.add_child(Node.s32('param_num_5', 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_1", ""))
|
||||||
info.add_child(Node.string('param_str_2', ''))
|
info.add_child(Node.string("param_str_2", ""))
|
||||||
info.add_child(Node.string('param_str_3', ''))
|
info.add_child(Node.string("param_str_3", ""))
|
||||||
info.add_child(Node.string('param_str_4', ''))
|
info.add_child(Node.string("param_str_4", ""))
|
||||||
info.add_child(Node.string('param_str_5', ''))
|
info.add_child(Node.string("param_str_5", ""))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
# Refresh interval in seconds.
|
# Refresh interval in seconds.
|
||||||
game.add_child(Node.u32('interval', 10))
|
game.add_child(Node.u32("interval", 10))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
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:
|
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)
|
root = self.get_profile_by_refid(refid)
|
||||||
if root is not None:
|
if root is not None:
|
||||||
return root
|
return root
|
||||||
@ -294,12 +297,12 @@ class Museca1Plus(
|
|||||||
# Return the previous formatted profile to the game.
|
# Return the previous formatted profile to the game.
|
||||||
return previous_game.format_profile(userid, profile)
|
return previous_game.format_profile(userid, profile)
|
||||||
else:
|
else:
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
root.add_child(Node.u8('result', 1))
|
root.add_child(Node.u8("result", 1))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_3_load_m_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -307,160 +310,192 @@ class Museca1Plus(
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
if userid is not 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:
|
else:
|
||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
# Output to the game
|
# Output to the game
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
new = Node.void('new')
|
new = Node.void("new")
|
||||||
game.add_child(new)
|
game.add_child(new)
|
||||||
|
|
||||||
for score in scores:
|
for score in scores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
new.add_child(music)
|
new.add_child(music)
|
||||||
music.add_child(Node.u32('music_id', score.id))
|
music.add_child(Node.u32("music_id", score.id))
|
||||||
music.add_child(Node.u32('music_type', score.chart))
|
music.add_child(Node.u32("music_type", score.chart))
|
||||||
music.add_child(Node.u32('score', score.points))
|
music.add_child(Node.u32("score", score.points))
|
||||||
music.add_child(Node.u32('cnt', score.plays))
|
music.add_child(Node.u32("cnt", score.plays))
|
||||||
music.add_child(Node.u32('combo', score.data.get_int('combo')))
|
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(
|
||||||
music.add_child(Node.u32('score_grade', self.db_to_game_grade(score.data.get_int('grade'))))
|
Node.u32(
|
||||||
stats = score.data.get_dict('stats')
|
"clear_type",
|
||||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
self.db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||||
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(
|
||||||
|
"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
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
|
|
||||||
# Generic profile stuff
|
# Generic profile stuff
|
||||||
game.add_child(Node.string('name', profile.get_str('name')))
|
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.string("code", ID.format_extid(profile.extid)))
|
||||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
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("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.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(
|
||||||
game.add_child(Node.u32('blaster_energy', profile.get_int('blaster_energy')))
|
Node.s32_array("hidden_param", profile.get_int_array("hidden_param", 20))
|
||||||
game.add_child(Node.u32('blaster_count', profile.get_int('blaster_count')))
|
)
|
||||||
|
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
|
# Enable Ryusei Festa
|
||||||
ryusei_festa = Node.void('ryusei_festa')
|
ryusei_festa = Node.void("ryusei_festa")
|
||||||
game.add_child(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
|
# Play statistics
|
||||||
statistics = self.get_play_statistics(userid)
|
statistics = self.get_play_statistics(userid)
|
||||||
game.add_child(Node.u32('play_count', statistics.total_plays))
|
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("daily_count", statistics.today_plays))
|
||||||
game.add_child(Node.u32('play_chain', statistics.consecutive_days))
|
game.add_child(Node.u32("play_chain", statistics.consecutive_days))
|
||||||
|
|
||||||
# Last played stuff
|
# Last played stuff
|
||||||
if 'last' in profile:
|
if "last" in profile:
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
game.add_child(last)
|
game.add_child(last)
|
||||||
last.add_child(Node.s32('music_id', lastdict.get_int('music_id', -1)))
|
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("music_type", lastdict.get_int("music_type")))
|
||||||
last.add_child(Node.u8('sort_type', lastdict.get_int('sort_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("narrow_down", lastdict.get_int("narrow_down")))
|
||||||
last.add_child(Node.u8('headphone', lastdict.get_int('headphone')))
|
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("appeal_id", lastdict.get_int("appeal_id", 1001)))
|
||||||
last.add_child(Node.u16('comment_id', lastdict.get_int('comment_id')))
|
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.u8("gauge_option", lastdict.get_int("gauge_option")))
|
||||||
|
|
||||||
# Item unlocks
|
# Item unlocks
|
||||||
itemnode = Node.void('item')
|
itemnode = Node.void("item")
|
||||||
game.add_child(itemnode)
|
game.add_child(itemnode)
|
||||||
|
|
||||||
game_config = self.get_game_config()
|
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:
|
for item in achievements:
|
||||||
if item.type[:5] != 'item_':
|
if item.type[:5] != "item_":
|
||||||
continue
|
continue
|
||||||
itemtype = int(item.type[5:])
|
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
|
# Don't echo unlocked songs, we will add all of them later
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
itemnode.add_child(info)
|
itemnode.add_child(info)
|
||||||
info.add_child(Node.u8('type', itemtype))
|
info.add_child(Node.u8("type", itemtype))
|
||||||
info.add_child(Node.u32('id', item.id))
|
info.add_child(Node.u32("id", item.id))
|
||||||
info.add_child(Node.u32('param', item.data.get_int('param')))
|
info.add_child(Node.u32("param", item.data.get_int("param")))
|
||||||
if 'diff_param' in item.data:
|
if "diff_param" in item.data:
|
||||||
info.add_child(Node.s32('diff_param', item.data.get_int('diff_param')))
|
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] = {}
|
ids: Dict[int, int] = {}
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
songs = self.data.local.music.get_all_songs(self.game, self.music_version)
|
||||||
for song in songs:
|
for song in songs:
|
||||||
if song.id not in ids:
|
if song.id not in ids:
|
||||||
ids[song.id] = 0
|
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)
|
ids[song.id] = ids[song.id] | (1 << song.chart)
|
||||||
|
|
||||||
for itemid in ids:
|
for itemid in ids:
|
||||||
if ids[itemid] == 0:
|
if ids[itemid] == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
itemnode.add_child(info)
|
itemnode.add_child(info)
|
||||||
info.add_child(Node.u8('type', self.GAME_CATALOG_TYPE_SONG))
|
info.add_child(Node.u8("type", self.GAME_CATALOG_TYPE_SONG))
|
||||||
info.add_child(Node.u32('id', itemid))
|
info.add_child(Node.u32("id", itemid))
|
||||||
info.add_child(Node.u32('param', ids[itemid]))
|
info.add_child(Node.u32("param", ids[itemid]))
|
||||||
|
|
||||||
return game
|
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()
|
newprofile = oldprofile.clone()
|
||||||
|
|
||||||
# Update blaster energy and in-game currencies
|
# 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:
|
if earned_gamecoin_packet is not None:
|
||||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
newprofile.replace_int(
|
||||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||||
|
)
|
||||||
|
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||||
if earned_gamecoin_block is not None:
|
if earned_gamecoin_block is not None:
|
||||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
newprofile.replace_int(
|
||||||
earned_blaster_energy = request.child_value('earned_blaster_energy')
|
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||||
|
)
|
||||||
|
earned_blaster_energy = request.child_value("earned_blaster_energy")
|
||||||
if earned_blaster_energy is not None:
|
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
|
# Miscelaneous stuff
|
||||||
newprofile.replace_int('blaster_count', request.child_value('blaster_count'))
|
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("skill_name_id", request.child_value("skill_name_id"))
|
||||||
newprofile.replace_int_array('hidden_param', 20, request.child_value('hidden_param'))
|
newprofile.replace_int_array(
|
||||||
|
"hidden_param", 20, request.child_value("hidden_param")
|
||||||
|
)
|
||||||
|
|
||||||
# Update user's unlock status if we aren't force unlocked
|
# Update user's unlock status if we aren't force unlocked
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
|
|
||||||
if request.child('item') is not None:
|
if request.child("item") is not None:
|
||||||
for child in request.child('item').children:
|
for child in request.child("item").children:
|
||||||
if child.name != 'info':
|
if child.name != "info":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_id = child.child_value('id')
|
item_id = child.child_value("id")
|
||||||
item_type = child.child_value('type')
|
item_type = child.child_value("type")
|
||||||
param = child.child_value('param')
|
param = child.child_value("param")
|
||||||
diff_param = child.child_value('diff_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
|
# Don't save back songs, because they were force unlocked
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if diff_param is not None:
|
if diff_param is not None:
|
||||||
paramvals = {
|
paramvals = {
|
||||||
'diff_param': diff_param,
|
"diff_param": diff_param,
|
||||||
'param': param,
|
"param": param,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
paramvals = {
|
paramvals = {
|
||||||
'param': param,
|
"param": param,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
@ -468,23 +503,23 @@ class Museca1Plus(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
item_id,
|
item_id,
|
||||||
f'item_{item_type}',
|
f"item_{item_type}",
|
||||||
paramvals,
|
paramvals,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Grab last information.
|
# Grab last information.
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||||
lastdict.replace_int('comment_id', request.child_value('comment_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_id", request.child_value("music_id"))
|
||||||
lastdict.replace_int('music_type', request.child_value('music_type'))
|
lastdict.replace_int("music_type", request.child_value("music_type"))
|
||||||
lastdict.replace_int('sort_type', request.child_value('sort_type'))
|
lastdict.replace_int("sort_type", request.child_value("sort_type"))
|
||||||
lastdict.replace_int('narrow_down', request.child_value('narrow_down'))
|
lastdict.replace_int("narrow_down", request.child_value("narrow_down"))
|
||||||
lastdict.replace_int('gauge_option', request.child_value('gauge_option'))
|
lastdict.replace_int("gauge_option", request.child_value("gauge_option"))
|
||||||
|
|
||||||
# Save back last information gleaned from results
|
# Save back last information gleaned from results
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Keep track of play statistics
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
@ -4,7 +4,14 @@ from typing_extensions import Final
|
|||||||
|
|
||||||
from bemani.backend.base import Base
|
from bemani.backend.base import Base
|
||||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
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.data import UserID, Achievement, ScoreSaveException
|
||||||
from bemani.protocol import Node
|
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 medals, as saved into/loaded from the DB
|
||||||
PLAY_MEDAL_NO_PLAY: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_NO_PLAY
|
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_CIRCLE_FAILED: Final[
|
||||||
PLAY_MEDAL_DIAMOND_FAILED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED
|
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_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_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_CIRCLE_CLEARED: Final[
|
||||||
PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED
|
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_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_CIRCLE_FULL_COMBO: Final[
|
||||||
PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO
|
int
|
||||||
PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO
|
] = 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
|
PLAY_MEDAL_PERFECT: Final[int] = DBConstants.POPN_MUSIC_PLAY_MEDAL_PERFECT
|
||||||
|
|
||||||
# Chart type, as saved into/loaded from the DB, and returned to game
|
# Chart type, as saved into/loaded from the DB, and returned to game
|
||||||
@ -47,7 +68,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
# properly.
|
# properly.
|
||||||
supports_expired_profiles: bool = False
|
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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -59,7 +80,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
Base handler for a profile. Given a userid and a profile dictionary,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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:
|
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
|
format_conversion on that previous version to get the profile to
|
||||||
migrate.
|
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,
|
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.
|
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
|
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
|
Given a RefID, return a formatted profile node. Basically every game
|
||||||
needs a profile lookup, even if it handles where that happens in
|
needs a profile lookup, even if it handles where that happens in
|
||||||
@ -138,7 +163,7 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'なし'
|
name = "なし"
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
@ -149,11 +174,11 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if chara is not None:
|
if chara is not None:
|
||||||
profile.replace_int('chara', chara)
|
profile.replace_int("chara", chara)
|
||||||
self.put_profile(userid, profile)
|
self.put_profile(userid, profile)
|
||||||
for achievement in achievements:
|
for achievement in achievements:
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
@ -227,19 +252,19 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Replace medal with highest value
|
# Replace medal with highest value
|
||||||
scoredata.replace_int('medal', max(scoredata.get_int('medal'), medal))
|
scoredata.replace_int("medal", max(scoredata.get_int("medal"), medal))
|
||||||
history.replace_int('medal', medal)
|
history.replace_int("medal", medal)
|
||||||
|
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
if raised:
|
if raised:
|
||||||
# We have stats, and there's a new high score, update the stats
|
# We have stats, and there's a new high score, update the stats
|
||||||
scoredata.replace_dict('stats', stats)
|
scoredata.replace_dict("stats", stats)
|
||||||
history.replace_dict('stats', stats)
|
history.replace_dict("stats", stats)
|
||||||
|
|
||||||
if combo is not None:
|
if combo is not None:
|
||||||
# If we have a combo, replace it
|
# If we have a combo, replace it
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
@ -290,44 +315,53 @@ class PopnMusicBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
# We saved successfully
|
# We saved successfully
|
||||||
break
|
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
|
# Generate scorecard
|
||||||
profile = self.get_profile(userid)
|
profile = self.get_profile(userid)
|
||||||
song = self.data.local.music.get_song(self.game, self.version, songid, chart)
|
song = self.data.local.music.get_song(self.game, self.version, songid, chart)
|
||||||
|
|
||||||
card_medal = {
|
card_medal = {
|
||||||
self.PLAY_MEDAL_CIRCLE_FAILED: 'Failed',
|
self.PLAY_MEDAL_CIRCLE_FAILED: "Failed",
|
||||||
self.PLAY_MEDAL_DIAMOND_FAILED: 'Failed',
|
self.PLAY_MEDAL_DIAMOND_FAILED: "Failed",
|
||||||
self.PLAY_MEDAL_STAR_FAILED: 'Failed',
|
self.PLAY_MEDAL_STAR_FAILED: "Failed",
|
||||||
self.PLAY_MEDAL_EASY_CLEAR: 'Cleared',
|
self.PLAY_MEDAL_EASY_CLEAR: "Cleared",
|
||||||
self.PLAY_MEDAL_CIRCLE_CLEARED: 'Cleared',
|
self.PLAY_MEDAL_CIRCLE_CLEARED: "Cleared",
|
||||||
self.PLAY_MEDAL_DIAMOND_CLEARED: 'Cleared',
|
self.PLAY_MEDAL_DIAMOND_CLEARED: "Cleared",
|
||||||
self.PLAY_MEDAL_STAR_CLEARED: 'Cleared',
|
self.PLAY_MEDAL_STAR_CLEARED: "Cleared",
|
||||||
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: 'Full Combo',
|
self.PLAY_MEDAL_CIRCLE_FULL_COMBO: "Full Combo",
|
||||||
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: 'Full Combo',
|
self.PLAY_MEDAL_DIAMOND_FULL_COMBO: "Full Combo",
|
||||||
self.PLAY_MEDAL_STAR_FULL_COMBO: 'Full Combo',
|
self.PLAY_MEDAL_STAR_FULL_COMBO: "Full Combo",
|
||||||
self.PLAY_MEDAL_PERFECT: 'Perfect',
|
self.PLAY_MEDAL_PERFECT: "Perfect",
|
||||||
}[medal]
|
}[medal]
|
||||||
|
|
||||||
card_chart = {
|
card_chart = {
|
||||||
self.CHART_TYPE_EASY: 'Easy',
|
self.CHART_TYPE_EASY: "Easy",
|
||||||
self.CHART_TYPE_NORMAL: 'Normal',
|
self.CHART_TYPE_NORMAL: "Normal",
|
||||||
self.CHART_TYPE_HYPER: 'Hyper',
|
self.CHART_TYPE_HYPER: "Hyper",
|
||||||
self.CHART_TYPE_EX: 'Ex',
|
self.CHART_TYPE_EX: "Ex",
|
||||||
}[chart]
|
}[chart]
|
||||||
|
|
||||||
# Construct the dictionary for the broadcast
|
# Construct the dictionary for the broadcast
|
||||||
card_data = {
|
card_data = {
|
||||||
BroadcastConstants.PLAYER_NAME: profile.get_str('name', 'なし'),
|
BroadcastConstants.PLAYER_NAME: profile.get_str("name", "なし"),
|
||||||
BroadcastConstants.SONG_NAME: song.name,
|
BroadcastConstants.SONG_NAME: song.name,
|
||||||
BroadcastConstants.ARTIST_NAME: song.artist,
|
BroadcastConstants.ARTIST_NAME: song.artist,
|
||||||
BroadcastConstants.DIFFICULTY: card_chart,
|
BroadcastConstants.DIFFICULTY: card_chart,
|
||||||
BroadcastConstants.SCORE: str(points),
|
BroadcastConstants.SCORE: str(points),
|
||||||
BroadcastConstants.MEDAL: card_medal,
|
BroadcastConstants.MEDAL: card_medal,
|
||||||
BroadcastConstants.COOLS: str(stats['cool']),
|
BroadcastConstants.COOLS: str(stats["cool"]),
|
||||||
BroadcastConstants.GREATS: str(stats['great']),
|
BroadcastConstants.GREATS: str(stats["great"]),
|
||||||
BroadcastConstants.GOODS: str(stats['good']),
|
BroadcastConstants.GOODS: str(stats["good"]),
|
||||||
BroadcastConstants.BADS: str(stats['bad']),
|
BroadcastConstants.BADS: str(stats["bad"]),
|
||||||
BroadcastConstants.COMBO: str(combo),
|
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
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
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)
|
Base.register(gamecode, PopnMusicFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date <= 2014061900:
|
if date <= 2014061900:
|
||||||
return VersionConstants.POPN_MUSIC_SUNNY_PARK
|
return VersionConstants.POPN_MUSIC_SUNNY_PARK
|
||||||
@ -87,26 +92,34 @@ class PopnMusicFactory(Factory):
|
|||||||
return VersionConstants.POPN_MUSIC_KAIMEI_RIDDLES
|
return VersionConstants.POPN_MUSIC_KAIMEI_RIDDLES
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'G15':
|
if model.gamecode == "G15":
|
||||||
return PopnMusicAdventure(data, config, model)
|
return PopnMusicAdventure(data, config, model)
|
||||||
if model.gamecode == 'H16':
|
if model.gamecode == "H16":
|
||||||
return PopnMusicParty(data, config, model)
|
return PopnMusicParty(data, config, model)
|
||||||
if model.gamecode == 'I17':
|
if model.gamecode == "I17":
|
||||||
return PopnMusicTheMovie(data, config, model)
|
return PopnMusicTheMovie(data, config, model)
|
||||||
if model.gamecode == 'J39':
|
if model.gamecode == "J39":
|
||||||
return PopnMusicSengokuRetsuden(data, config, model)
|
return PopnMusicSengokuRetsuden(data, config, model)
|
||||||
if model.gamecode == 'K39':
|
if model.gamecode == "K39":
|
||||||
return PopnMusicTuneStreet(data, config, model)
|
return PopnMusicTuneStreet(data, config, model)
|
||||||
if model.gamecode == 'L39':
|
if model.gamecode == "L39":
|
||||||
return PopnMusicFantasia(data, config, model)
|
return PopnMusicFantasia(data, config, model)
|
||||||
if model.gamecode == 'M39':
|
if model.gamecode == "M39":
|
||||||
if model.version is None:
|
if model.version is None:
|
||||||
if parentmodel is None:
|
if parentmodel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# We have no way to tell apart newer versions. However, we can make
|
# 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.
|
# 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
|
return None
|
||||||
parentversion = version_from_date(parentmodel.version)
|
parentversion = version_from_date(parentmodel.version)
|
||||||
if parentversion == VersionConstants.POPN_MUSIC_LAPISTORIA:
|
if parentversion == VersionConstants.POPN_MUSIC_LAPISTORIA:
|
||||||
|
@ -52,31 +52,31 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'ints': [
|
"ints": [
|
||||||
{
|
{
|
||||||
'name': 'Game Phase',
|
"name": "Game Phase",
|
||||||
'tip': 'Game unlock phase for all players.',
|
"tip": "Game unlock phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'game_phase',
|
"setting": "game_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'NO PHASE',
|
0: "NO PHASE",
|
||||||
1: 'SECRET DATA RELEASE',
|
1: "SECRET DATA RELEASE",
|
||||||
2: 'MAX: ALL DATA RELEASE',
|
2: "MAX: ALL DATA RELEASE",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Pop\'n Quest Event Phase',
|
"name": "Pop'n Quest Event Phase",
|
||||||
'tip': 'Event phase for all players.',
|
"tip": "Event phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'event_phase',
|
"setting": "event_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'No event',
|
0: "No event",
|
||||||
1: 'Phase 1',
|
1: "Phase 1",
|
||||||
2: 'Phase 2',
|
2: "Phase 2",
|
||||||
3: 'Phase 3',
|
3: "Phase 3",
|
||||||
4: 'Phase 4',
|
4: "Phase 4",
|
||||||
5: 'Phase MAX',
|
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_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_STAR_FULL_COMBO: self.GAME_PLAY_MEDAL_STAR_FULL_COMBO,
|
||||||
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
|
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_MEDAL_PERFECT,
|
||||||
}[score.data.get_int('medal')]
|
}[score.data.get_int("medal")]
|
||||||
position = {
|
position = {
|
||||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_POSITION,
|
||||||
@ -104,67 +104,95 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
return medal << (position * 4)
|
return medal << (position * 4)
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
|
|
||||||
# Set up the base profile
|
# Set up the base profile
|
||||||
base = Node.void('base')
|
base = Node.void("base")
|
||||||
root.add_child(base)
|
root.add_child(base)
|
||||||
base.add_child(Node.string('name', profile.get_str('name', 'なし')))
|
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.string("g_pm_id", ID.format_extid(profile.extid)))
|
||||||
base.add_child(Node.u8('mode', profile.get_int('mode', 0)))
|
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("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.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(
|
||||||
base.add_child(Node.s8('category', profile.get_int('category', -1)))
|
Node.u8("medal_and_friend", profile.get_int("medal_and_friend", 0))
|
||||||
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("category", profile.get_int("category", -1)))
|
||||||
base.add_child(Node.s8('chara_category', profile.get_int('chara_category', -1)))
|
base.add_child(Node.s8("sub_category", profile.get_int("sub_category", -1)))
|
||||||
base.add_child(Node.u8('collabo', profile.get_int('collabo', 255)))
|
base.add_child(Node.s16("chara", profile.get_int("chara", -1)))
|
||||||
base.add_child(Node.u8('sheet', profile.get_int('sheet', 0)))
|
base.add_child(Node.s8("chara_category", profile.get_int("chara_category", -1)))
|
||||||
base.add_child(Node.s8('tutorial', profile.get_int('tutorial', 0)))
|
base.add_child(Node.u8("collabo", profile.get_int("collabo", 255)))
|
||||||
base.add_child(Node.s32('music_open_pt', profile.get_int('music_open_pt', 0)))
|
base.add_child(Node.u8("sheet", profile.get_int("sheet", 0)))
|
||||||
base.add_child(Node.s8('is_conv', -1))
|
base.add_child(Node.s8("tutorial", profile.get_int("tutorial", 0)))
|
||||||
base.add_child(Node.s32('option', profile.get_int('option', 0)))
|
base.add_child(Node.s32("music_open_pt", profile.get_int("music_open_pt", 0)))
|
||||||
base.add_child(Node.s16('music', profile.get_int('music', -1)))
|
base.add_child(Node.s8("is_conv", -1))
|
||||||
base.add_child(Node.u16('ep', profile.get_int('ep', 0)))
|
base.add_child(Node.s32("option", profile.get_int("option", 0)))
|
||||||
base.add_child(Node.s32_array('sp_color_flg', profile.get_int_array('sp_color_flg', 2)))
|
base.add_child(Node.s16("music", profile.get_int("music", -1)))
|
||||||
base.add_child(Node.s32('read_news', profile.get_int('read_news', 0)))
|
base.add_child(Node.u16("ep", profile.get_int("ep", 0)))
|
||||||
base.add_child(Node.s16('consecutive_days_coupon', profile.get_int('consecutive_days_coupon', 0)))
|
base.add_child(
|
||||||
base.add_child(Node.s8('staff', 0))
|
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 section
|
||||||
player_card_dict = profile.get_dict('player_card')
|
player_card_dict = profile.get_dict("player_card")
|
||||||
player_card = Node.void('player_card')
|
player_card = Node.void("player_card")
|
||||||
root.add_child(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(
|
||||||
player_card.add_child(Node.u8('frame', player_card_dict.get_int('frame')))
|
Node.u8_array("title", player_card_dict.get_int_array("title", 2, [0, 1]))
|
||||||
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.u8("frame", player_card_dict.get_int("frame")))
|
||||||
player_card.add_child(Node.s32_array('get_title', player_card_dict.get_int_array('get_title', 4)))
|
player_card.add_child(Node.u8("base", player_card_dict.get_int("base")))
|
||||||
player_card.add_child(Node.s32('get_frame', player_card_dict.get_int('get_frame')))
|
player_card.add_child(
|
||||||
player_card.add_child(Node.s32('get_base', player_card_dict.get_int('get_base')))
|
Node.u8_array("seal", player_card_dict.get_int_array("seal", 2))
|
||||||
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.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 section
|
||||||
player_card_ex = Node.void('player_card_ex')
|
player_card_ex = Node.void("player_card_ex")
|
||||||
root.add_child(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(
|
||||||
player_card_ex.add_child(Node.s32('get_frame_ex', player_card_dict.get_int('get_frame_ex')))
|
Node.s32("get_title_ex", player_card_dict.get_int("get_title_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_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 section and scores section
|
||||||
statistics = self.get_play_statistics(userid)
|
statistics = self.get_play_statistics(userid)
|
||||||
base.add_child(Node.s32('total_play_cnt', statistics.total_plays))
|
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("today_play_cnt", statistics.today_plays))
|
||||||
base.add_child(Node.s16('consecutive_days', statistics.consecutive_days))
|
base.add_child(Node.s16("consecutive_days", statistics.consecutive_days))
|
||||||
|
|
||||||
# Number of rivals that are active for this version.
|
# Number of rivals that are active for this version.
|
||||||
links = self.data.local.user.get_links(self.game, self.version, userid)
|
links = self.data.local.user.get_links(self.game, self.version, userid)
|
||||||
rivalcount = 0
|
rivalcount = 0
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type != 'rival':
|
if link.type != "rival":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not self.has_profile(link.other_userid):
|
if not self.has_profile(link.other_userid):
|
||||||
@ -172,10 +200,20 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
|
|
||||||
# This profile is valid.
|
# This profile is valid.
|
||||||
rivalcount += 1
|
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)]
|
last_played = [
|
||||||
most_played = [x[0] for x in self.data.local.music.get_most_played(self.game, self.version, userid, 20)]
|
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:
|
while len(last_played) < 3:
|
||||||
last_played.append(-1)
|
last_played.append(-1)
|
||||||
while len(most_played) < 20:
|
while len(most_played) < 20:
|
||||||
@ -198,10 +236,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||||
continue
|
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) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_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_byte_pos = int((hiscore_index * 17) / 8)
|
||||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||||
hiscore_value = score.points << hiscore_bit_pos
|
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] = hiscore_array[hiscore_byte_pos] | (
|
||||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
hiscore_value & 0xFF
|
||||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 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)
|
hiscore = bytes(hiscore_array)
|
||||||
|
|
||||||
player_card.add_child(Node.s16_array('best_music', most_played[0:3]))
|
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("my_best", most_played))
|
||||||
base.add_child(Node.s16_array('latest_music', last_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.u16_array("clear_medal", clear_medal))
|
||||||
base.add_child(Node.u8_array('clear_medal_sub', clear_medal_sub))
|
base.add_child(Node.u8_array("clear_medal_sub", clear_medal_sub))
|
||||||
|
|
||||||
# Goes outside of base for some reason
|
# Goes outside of base for some reason
|
||||||
root.add_child(Node.binary('hiscore', hiscore))
|
root.add_child(Node.binary("hiscore", hiscore))
|
||||||
|
|
||||||
# Net VS section
|
# Net VS section
|
||||||
netvs = Node.void('netvs')
|
netvs = Node.void("netvs")
|
||||||
root.add_child(netvs)
|
root.add_child(netvs)
|
||||||
netvs.add_child(Node.s32_array('get_ojama', [0, 0]))
|
netvs.add_child(Node.s32_array("get_ojama", [0, 0]))
|
||||||
netvs.add_child(Node.s32('rank_point', 0))
|
netvs.add_child(Node.s32("rank_point", 0))
|
||||||
netvs.add_child(Node.s32('play_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.s16_array("record", [0, 0, 0, 0, 0, 0]))
|
||||||
netvs.add_child(Node.u8('rank', 0))
|
netvs.add_child(Node.u8("rank", 0))
|
||||||
netvs.add_child(Node.s8_array('ojama_condition', [0] * 74))
|
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_ojama", [0, 0, 0]))
|
||||||
netvs.add_child(Node.s8_array('set_recommend', [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.s8_array("jewelry", [0] * 15))
|
||||||
for dialog in [0, 1, 2, 3, 4, 5]:
|
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)
|
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)
|
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
|
# Navigate section
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
navigate_dict = profile.get_dict(f'navigate_{i}')
|
navigate_dict = profile.get_dict(f"navigate_{i}")
|
||||||
navigate = Node.void('navigate')
|
navigate = Node.void("navigate")
|
||||||
root.add_child(navigate)
|
root.add_child(navigate)
|
||||||
navigate.add_child(Node.s8('genre', navigate_dict.get_int('genre', -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("image", navigate_dict.get_int("image", -1)))
|
||||||
navigate.add_child(Node.s8('level', navigate_dict.get_int('level', -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.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(
|
||||||
navigate.add_child(Node.s8('button', navigate_dict.get_int('button', -1)))
|
Node.s16("limit_num", navigate_dict.get_int("limit_num", -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("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
|
return root
|
||||||
|
|
||||||
def format_conversion(self, userid: UserID, profile: Profile) -> Node:
|
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.string("name", profile.get_str("name", "なし")))
|
||||||
root.add_child(Node.s16('chara', profile.get_int('chara', -1)))
|
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.s32("option", profile.get_int("option", 0)))
|
||||||
root.add_child(Node.u8('version', 0))
|
root.add_child(Node.u8("version", 0))
|
||||||
root.add_child(Node.u8('kind', 0))
|
root.add_child(Node.u8("kind", 0))
|
||||||
root.add_child(Node.u8('season', 0))
|
root.add_child(Node.u8("season", 0))
|
||||||
|
|
||||||
clear_medal = [0] * self.GAME_MAX_MUSIC_ID
|
clear_medal = [0] * self.GAME_MAX_MUSIC_ID
|
||||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
||||||
@ -291,10 +343,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||||
continue
|
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) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_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_byte_pos = int((hiscore_index * 17) / 8)
|
||||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||||
hiscore_value = score.points << hiscore_bit_pos
|
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] = hiscore_array[hiscore_byte_pos] | (
|
||||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
hiscore_value & 0xFF
|
||||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 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.u16_array("clear_medal", clear_medal))
|
||||||
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
|
root.add_child(Node.binary("hiscore", bytes(hiscore_array)))
|
||||||
|
|
||||||
return root
|
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'
|
# 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
|
# 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.
|
# 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
|
return oldprofile
|
||||||
|
|
||||||
newprofile = oldprofile.clone()
|
newprofile = oldprofile.clone()
|
||||||
newprofile.replace_int('option', request.child_value('option'))
|
newprofile.replace_int("option", request.child_value("option"))
|
||||||
newprofile.replace_int('chara', request.child_value('chara'))
|
newprofile.replace_int("chara", request.child_value("chara"))
|
||||||
newprofile.replace_int('mode', request.child_value('mode'))
|
newprofile.replace_int("mode", request.child_value("mode"))
|
||||||
newprofile.replace_int('button', request.child_value('button'))
|
newprofile.replace_int("button", request.child_value("button"))
|
||||||
newprofile.replace_int('music', request.child_value('music'))
|
newprofile.replace_int("music", request.child_value("music"))
|
||||||
newprofile.replace_int('sheet', request.child_value('sheet'))
|
newprofile.replace_int("sheet", request.child_value("sheet"))
|
||||||
newprofile.replace_int('last_play_flag', request.child_value('last_play_flag'))
|
newprofile.replace_int("last_play_flag", request.child_value("last_play_flag"))
|
||||||
newprofile.replace_int('category', request.child_value('category'))
|
newprofile.replace_int("category", request.child_value("category"))
|
||||||
newprofile.replace_int('sub_category', request.child_value('sub_category'))
|
newprofile.replace_int("sub_category", request.child_value("sub_category"))
|
||||||
newprofile.replace_int('chara_category', request.child_value('chara_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(
|
||||||
newprofile.replace_int('ep', request.child_value('ep'))
|
"medal_and_friend", request.child_value("medal_and_friend")
|
||||||
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("ep", request.child_value("ep"))
|
||||||
newprofile.replace_int('consecutive_days_coupon', request.child_value('consecutive_days_coupon'))
|
newprofile.replace_int_array(
|
||||||
newprofile.replace_int('tutorial', request.child_value('tutorial'))
|
"sp_color_flg", 2, request.child_value("sp_color_flg")
|
||||||
newprofile.replace_int('music_open_pt', request.child_value('music_open_pt'))
|
)
|
||||||
newprofile.replace_int('collabo', request.child_value('collabo'))
|
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:
|
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:
|
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
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
|
||||||
# Extract player card stuff
|
# Extract player card stuff
|
||||||
player_card_dict = newprofile.get_dict('player_card')
|
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_array("title", 2, request.child_value("title"))
|
||||||
player_card_dict.replace_int('frame', request.child_value('frame'))
|
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("base", request.child_value("base"))
|
||||||
player_card_dict.replace_int_array('seal', 2, request.child_value('seal'))
|
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_array(
|
||||||
player_card_dict.replace_int('get_frame', request.child_value('get_frame'))
|
"get_title", 4, request.child_value("get_title")
|
||||||
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.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:
|
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(
|
||||||
player_card_dict.replace_int('get_frame_ex', player_card_ex.child_value('get_frame_ex'))
|
"get_title_ex", player_card_ex.child_value("get_title_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'))
|
player_card_dict.replace_int(
|
||||||
newprofile.replace_dict('player_card', player_card_dict)
|
"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
|
# Extract navigate stuff
|
||||||
nav_id = 0
|
nav_id = 0
|
||||||
for navigate in request.children:
|
for navigate in request.children:
|
||||||
if navigate.name == 'navigate':
|
if navigate.name == "navigate":
|
||||||
navigate_dict = newprofile.get_dict(f'navigate_{nav_id}')
|
navigate_dict = newprofile.get_dict(f"navigate_{nav_id}")
|
||||||
navigate_dict.replace_int('genre', navigate.child_value('genre'))
|
navigate_dict.replace_int("genre", navigate.child_value("genre"))
|
||||||
navigate_dict.replace_int('image', navigate.child_value('image'))
|
navigate_dict.replace_int("image", navigate.child_value("image"))
|
||||||
navigate_dict.replace_int('level', navigate.child_value('level'))
|
navigate_dict.replace_int("level", navigate.child_value("level"))
|
||||||
navigate_dict.replace_int('ojama', navigate.child_value('ojama'))
|
navigate_dict.replace_int("ojama", navigate.child_value("ojama"))
|
||||||
navigate_dict.replace_int('limit_num', navigate.child_value('limit_num'))
|
navigate_dict.replace_int(
|
||||||
navigate_dict.replace_int('button', navigate.child_value('button'))
|
"limit_num", navigate.child_value("limit_num")
|
||||||
navigate_dict.replace_int('life', navigate.child_value('life'))
|
)
|
||||||
navigate_dict.replace_int('progress', navigate.child_value('progress'))
|
navigate_dict.replace_int("button", navigate.child_value("button"))
|
||||||
newprofile.replace_dict(f'navigate_{nav_id}', navigate_dict)
|
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
|
nav_id += 1
|
||||||
|
|
||||||
if nav_id >= 3:
|
if nav_id >= 3:
|
||||||
@ -391,15 +473,15 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
|
|
||||||
# Extract scores
|
# Extract scores
|
||||||
for node in request.children:
|
for node in request.children:
|
||||||
if node.name == 'stage':
|
if node.name == "stage":
|
||||||
songid = node.child_value('no')
|
songid = node.child_value("no")
|
||||||
chart = {
|
chart = {
|
||||||
self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
|
self.GAME_CHART_TYPE_EASY: self.CHART_TYPE_EASY,
|
||||||
self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
|
self.GAME_CHART_TYPE_NORMAL: self.CHART_TYPE_NORMAL,
|
||||||
self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
|
self.GAME_CHART_TYPE_HYPER: self.CHART_TYPE_HYPER,
|
||||||
self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
|
self.GAME_CHART_TYPE_EX: self.CHART_TYPE_EX,
|
||||||
}[node.child_value('sheet')]
|
}[node.child_value("sheet")]
|
||||||
medal = (node.child_value('n_data') >> (chart * 4)) & 0x000F
|
medal = (node.child_value("n_data") >> (chart * 4)) & 0x000F
|
||||||
medal = {
|
medal = {
|
||||||
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
|
self.GAME_PLAY_MEDAL_CIRCLE_FAILED: self.PLAY_MEDAL_CIRCLE_FAILED,
|
||||||
self.GAME_PLAY_MEDAL_DIAMOND_FAILED: self.PLAY_MEDAL_DIAMOND_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_STAR_FULL_COMBO: self.PLAY_MEDAL_STAR_FULL_COMBO,
|
||||||
self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
|
self.GAME_PLAY_MEDAL_PERFECT: self.PLAY_MEDAL_PERFECT,
|
||||||
}[medal]
|
}[medal]
|
||||||
points = node.child_value('score')
|
points = node.child_value("score")
|
||||||
self.update_score(userid, songid, chart, points, medal)
|
self.update_score(userid, songid, chart, points, medal)
|
||||||
|
|
||||||
return newprofile
|
return newprofile
|
||||||
|
|
||||||
def handle_playerdata_expire_request(self, request: Node) -> Node:
|
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:
|
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:
|
def handle_playerdata_get_request(self, request: Node) -> Node:
|
||||||
modelstring = request.attribute('model')
|
modelstring = request.attribute("model")
|
||||||
refid = request.child_value('ref_id')
|
refid = request.child_value("ref_id")
|
||||||
root = self.get_profile_by_refid(
|
root = self.get_profile_by_refid(
|
||||||
refid,
|
refid,
|
||||||
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
||||||
)
|
)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_conversion_request(self, request: Node) -> Node:
|
def handle_playerdata_conversion_request(self, request: Node) -> Node:
|
||||||
refid = request.child_value('ref_id')
|
refid = request.child_value("ref_id")
|
||||||
name = request.child_value('name')
|
name = request.child_value("name")
|
||||||
chara = request.child_value('chara')
|
chara = request.child_value("chara")
|
||||||
root = self.new_profile_by_refid(refid, name, chara)
|
root = self.new_profile_by_refid(refid, name, chara)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_new_request(self, request: Node) -> Node:
|
def handle_playerdata_new_request(self, request: Node) -> Node:
|
||||||
refid = request.child_value('ref_id')
|
refid = request.child_value("ref_id")
|
||||||
name = request.child_value('name')
|
name = request.child_value("name")
|
||||||
root = self.new_profile_by_refid(refid, name)
|
root = self.new_profile_by_refid(refid, name)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_set_request(self, request: Node) -> Node:
|
def handle_playerdata_set_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('ref_id')
|
refid = request.attribute("ref_id")
|
||||||
machine = self.get_machine()
|
machine = self.get_machine()
|
||||||
|
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.add_child(Node.s8('pref', machine.data.get_int('pref', self.get_machine_region())))
|
root.add_child(
|
||||||
|
Node.s8("pref", machine.data.get_int("pref", self.get_machine_region()))
|
||||||
|
)
|
||||||
|
|
||||||
if refid is None:
|
if refid is None:
|
||||||
root.add_child(Node.string('name', ''))
|
root.add_child(Node.string("name", ""))
|
||||||
root.add_child(Node.s16('chara', -1))
|
root.add_child(Node.s16("chara", -1))
|
||||||
root.add_child(Node.u8('frame', 0))
|
root.add_child(Node.u8("frame", 0))
|
||||||
root.add_child(Node.u8('base', 0))
|
root.add_child(Node.u8("base", 0))
|
||||||
root.add_child(Node.u8('seal_1', 0))
|
root.add_child(Node.u8("seal_1", 0))
|
||||||
root.add_child(Node.u8('seal_2', 0))
|
root.add_child(Node.u8("seal_2", 0))
|
||||||
root.add_child(Node.u8('title_1', 0))
|
root.add_child(Node.u8("title_1", 0))
|
||||||
root.add_child(Node.u8('title_2', 0))
|
root.add_child(Node.u8("title_2", 0))
|
||||||
root.add_child(Node.s16('recommend_1', -1))
|
root.add_child(Node.s16("recommend_1", -1))
|
||||||
root.add_child(Node.s16('recommend_2', -1))
|
root.add_child(Node.s16("recommend_2", -1))
|
||||||
root.add_child(Node.s16('recommend_3', -1))
|
root.add_child(Node.s16("recommend_3", -1))
|
||||||
root.add_child(Node.string('message', ''))
|
root.add_child(Node.string("message", ""))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
root.add_child(Node.string('name', ''))
|
root.add_child(Node.string("name", ""))
|
||||||
root.add_child(Node.s16('chara', -1))
|
root.add_child(Node.s16("chara", -1))
|
||||||
root.add_child(Node.u8('frame', 0))
|
root.add_child(Node.u8("frame", 0))
|
||||||
root.add_child(Node.u8('base', 0))
|
root.add_child(Node.u8("base", 0))
|
||||||
root.add_child(Node.u8('seal_1', 0))
|
root.add_child(Node.u8("seal_1", 0))
|
||||||
root.add_child(Node.u8('seal_2', 0))
|
root.add_child(Node.u8("seal_2", 0))
|
||||||
root.add_child(Node.u8('title_1', 0))
|
root.add_child(Node.u8("title_1", 0))
|
||||||
root.add_child(Node.u8('title_2', 0))
|
root.add_child(Node.u8("title_2", 0))
|
||||||
root.add_child(Node.s16('recommend_1', -1))
|
root.add_child(Node.s16("recommend_1", -1))
|
||||||
root.add_child(Node.s16('recommend_2', -1))
|
root.add_child(Node.s16("recommend_2", -1))
|
||||||
root.add_child(Node.s16('recommend_3', -1))
|
root.add_child(Node.s16("recommend_3", -1))
|
||||||
root.add_child(Node.string('message', ''))
|
root.add_child(Node.string("message", ""))
|
||||||
return root
|
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)
|
newprofile = self.unformat_profile(userid, request, oldprofile)
|
||||||
|
|
||||||
if newprofile is not None:
|
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)
|
self.put_profile(userid, newprofile)
|
||||||
root.add_child(Node.string('name', newprofile.get_str('name', 'なし')))
|
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.s16("chara", newprofile.get_int("chara", -1)))
|
||||||
root.add_child(Node.u8('frame', player_card_dict.get_int('frame')))
|
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("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(
|
||||||
root.add_child(Node.u8('seal_2', player_card_dict.get_int_array('seal', 2)[1]))
|
Node.u8("seal_1", player_card_dict.get_int_array("seal", 2)[0])
|
||||||
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(
|
||||||
root.add_child(Node.s16('recommend_1', -1))
|
Node.u8("seal_2", player_card_dict.get_int_array("seal", 2)[1])
|
||||||
root.add_child(Node.s16('recommend_2', -1))
|
)
|
||||||
root.add_child(Node.s16('recommend_3', -1))
|
root.add_child(
|
||||||
root.add_child(Node.string('message', ''))
|
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
|
return root
|
||||||
|
|
||||||
def handle_playerdata_friend_request(self, request: Node) -> Node:
|
def handle_playerdata_friend_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('ref_id')
|
refid = request.attribute("ref_id")
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
|
|
||||||
# Look up our own user ID based on the RefID provided.
|
# Look up our own user ID based on the RefID provided.
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
# Grab the links that we care about.
|
# Grab the links that we care about.
|
||||||
@ -529,7 +627,7 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
profiles: Dict[UserID, Profile] = {}
|
profiles: Dict[UserID, Profile] = {}
|
||||||
rivals: List[Link] = []
|
rivals: List[Link] = []
|
||||||
for link in links:
|
for link in links:
|
||||||
if link.type != 'rival':
|
if link.type != "rival":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
other_profile = self.get_profile(link.other_userid)
|
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)
|
scores = self.data.remote.music.get_scores(self.game, self.version, rivalid)
|
||||||
|
|
||||||
# First, output general profile info.
|
# First, output general profile info.
|
||||||
friend = Node.void('friend')
|
friend = Node.void("friend")
|
||||||
root.add_child(friend)
|
root.add_child(friend)
|
||||||
|
|
||||||
# This might be for having non-active or non-confirmed friends, but setting to 0 makes the
|
# 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.
|
# 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.
|
# Set up some sane defaults.
|
||||||
friend.add_child(Node.string('name', rivalprofile.get_str('name', 'なし')))
|
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(
|
||||||
friend.add_child(Node.s16('chara', rivalprofile.get_int('chara', -1)))
|
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.
|
# Perform hiscore/medal conversion.
|
||||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 4) * 17) + 7) / 8)
|
||||||
@ -571,10 +671,12 @@ class PopnMusicFantasia(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EX,
|
self.CHART_TYPE_EX,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||||
continue
|
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) + {
|
hiscore_index = (score.id * 4) + {
|
||||||
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
self.CHART_TYPE_EASY: self.GAME_CHART_TYPE_EASY_POSITION,
|
||||||
self.CHART_TYPE_NORMAL: self.GAME_CHART_TYPE_NORMAL_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_byte_pos = int((hiscore_index * 17) / 8)
|
||||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||||
hiscore_value = score.points << hiscore_bit_pos
|
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] = hiscore_array[hiscore_byte_pos] | (
|
||||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
hiscore_value & 0xFF
|
||||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 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)
|
hiscore = bytes(hiscore_array)
|
||||||
friend.add_child(Node.u16_array('clear_medal', clear_medal))
|
friend.add_child(Node.u16_array("clear_medal", clear_medal))
|
||||||
friend.add_child(Node.binary('hiscore', hiscore))
|
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.
|
# 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:
|
def handle_game_get_request(self, request: Node) -> Node:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
game_phase = game_config.get_int('game_phase')
|
game_phase = game_config.get_int("game_phase")
|
||||||
event_phase = game_config.get_int('event_phase')
|
event_phase = game_config.get_int("event_phase")
|
||||||
|
|
||||||
root = Node.void('game')
|
root = Node.void("game")
|
||||||
root.add_child(Node.s32('game_phase', game_phase))
|
root.add_child(Node.s32("game_phase", game_phase))
|
||||||
root.add_child(Node.s32('ir_phase', 0))
|
root.add_child(Node.s32("ir_phase", 0))
|
||||||
root.add_child(Node.s32('event_phase', event_phase))
|
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(
|
||||||
root.add_child(Node.s32('card_phase', 6))
|
Node.s32("netvs_phase", 0)
|
||||||
root.add_child(Node.s32('illust_phase', 2))
|
) # Net taisen mode, we don't support lobbies.
|
||||||
root.add_child(Node.s32('psp_phase', 5)) # Unlock songs from Pop'n Music Portable.
|
root.add_child(Node.s32("card_phase", 6))
|
||||||
root.add_child(Node.s32('other_phase', 1))
|
root.add_child(Node.s32("illust_phase", 2))
|
||||||
root.add_child(Node.s32('jubeat_phase', 1))
|
root.add_child(
|
||||||
root.add_child(Node.s32('public_phase', 3))
|
Node.s32("psp_phase", 5)
|
||||||
root.add_child(Node.s32('kac_phase', 2))
|
) # Unlock songs from Pop'n Music Portable.
|
||||||
root.add_child(Node.s32('local_matching_enable', 1))
|
root.add_child(Node.s32("other_phase", 1))
|
||||||
root.add_child(Node.s32('n_matching_sec', 60))
|
root.add_child(Node.s32("jubeat_phase", 1))
|
||||||
root.add_child(Node.s32('l_matching_sec', 60))
|
root.add_child(Node.s32("public_phase", 3))
|
||||||
root.add_child(Node.s32('is_check_cpu', 0))
|
root.add_child(Node.s32("kac_phase", 2))
|
||||||
root.add_child(Node.s32('week_no', 0))
|
root.add_child(Node.s32("local_matching_enable", 1))
|
||||||
root.add_child(Node.s32('team_day', 0))
|
root.add_child(Node.s32("n_matching_sec", 60))
|
||||||
root.add_child(Node.s32_array('ng_illust', [-1] * 64))
|
root.add_child(Node.s32("l_matching_sec", 60))
|
||||||
root.add_child(Node.s16_array('sel_ranking', [-1] * 10))
|
root.add_child(Node.s32("is_check_cpu", 0))
|
||||||
root.add_child(Node.s16_array('up_ranking', [-1] * 10))
|
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
|
return root
|
||||||
|
|
||||||
def handle_game_active_request(self, request: Node) -> Node:
|
def handle_game_active_request(self, request: Node) -> Node:
|
||||||
# Update the name of this cab for admin purposes. Also store the prefecture.
|
# Update the name of this cab for admin purposes. Also store the prefecture.
|
||||||
machine = self.get_machine()
|
machine = self.get_machine()
|
||||||
machine.name = request.child_value('shop_name') or machine.name
|
machine.name = request.child_value("shop_name") or machine.name
|
||||||
machine.data.replace_int('pref', request.child_value('pref'))
|
machine.data.replace_int("pref", request.child_value("pref"))
|
||||||
self.update_machine(machine)
|
self.update_machine(machine)
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
def handle_lobby_requests(self, request: Node) -> Node:
|
def handle_lobby_requests(self, request: Node) -> Node:
|
||||||
# Stub out the entire lobby service
|
# 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 all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'ints': [
|
"ints": [
|
||||||
{
|
{
|
||||||
'name': 'Music Open Phase',
|
"name": "Music Open Phase",
|
||||||
'tip': 'Default music phase for all players.',
|
"tip": "Default music phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'music_phase',
|
"setting": "music_phase",
|
||||||
'values': {
|
"values": {
|
||||||
# The value goes to 30 now, but it starts where usaneko left off at 23
|
# The value goes to 30 now, but it starts where usaneko left off at 23
|
||||||
# Unlocks a total of 10 songs
|
# Unlocks a total of 10 songs
|
||||||
23: 'No music unlocks',
|
23: "No music unlocks",
|
||||||
24: 'Phase 1',
|
24: "Phase 1",
|
||||||
25: 'Phase 2',
|
25: "Phase 2",
|
||||||
26: 'Phase 3',
|
26: "Phase 3",
|
||||||
27: 'Phase 4',
|
27: "Phase 4",
|
||||||
28: 'Phase 5',
|
28: "Phase 5",
|
||||||
29: 'Phase 6',
|
29: "Phase 6",
|
||||||
30: 'Phase MAX',
|
30: "Phase MAX",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Kaimei! MN tanteisha event Phase',
|
"name": "Kaimei! MN tanteisha event Phase",
|
||||||
'tip': 'Kaimei! MN tanteisha event phase for all players.',
|
"tip": "Kaimei! MN tanteisha event phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'mn_tanteisha_phase',
|
"setting": "mn_tanteisha_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Disabled',
|
0: "Disabled",
|
||||||
1: 'Roki',
|
1: "Roki",
|
||||||
2: 'shiroro',
|
2: "shiroro",
|
||||||
3: 'PIERRE&JILL',
|
3: "PIERRE&JILL",
|
||||||
4: 'ROSA',
|
4: "ROSA",
|
||||||
5: 'taoxiang',
|
5: "taoxiang",
|
||||||
6: 'TangTang',
|
6: "TangTang",
|
||||||
7: 'OTOBEAR',
|
7: "OTOBEAR",
|
||||||
8: 'kaorin',
|
8: "kaorin",
|
||||||
9: 'CHARLY',
|
9: "CHARLY",
|
||||||
10: 'ALOE',
|
10: "ALOE",
|
||||||
11: 'RIE♥chan',
|
11: "RIE♥chan",
|
||||||
12: 'hina',
|
12: "hina",
|
||||||
13: 'PAPYRUS',
|
13: "PAPYRUS",
|
||||||
14: '雷蔵, miho, RIE♥chan, Ryusei Honey',
|
14: "雷蔵, miho, RIE♥chan, Ryusei Honey",
|
||||||
15: 'Murasaki',
|
15: "Murasaki",
|
||||||
16: 'Lucifelle',
|
16: "Lucifelle",
|
||||||
17: '六',
|
17: "六",
|
||||||
18: 'stella',
|
18: "stella",
|
||||||
19: 'ちせ',
|
19: "ちせ",
|
||||||
20: 'LISA',
|
20: "LISA",
|
||||||
21: 'SUMIRE',
|
21: "SUMIRE",
|
||||||
22: 'SHISHITUGU',
|
22: "SHISHITUGU",
|
||||||
23: 'WALKER',
|
23: "WALKER",
|
||||||
24: 'Candy',
|
24: "Candy",
|
||||||
25: 'Jade',
|
25: "Jade",
|
||||||
26: 'AYA',
|
26: "AYA",
|
||||||
27: 'kaorin',
|
27: "kaorin",
|
||||||
28: 'Lord Meh',
|
28: "Lord Meh",
|
||||||
29: 'HAMANOV',
|
29: "HAMANOV",
|
||||||
30: 'Agent',
|
30: "Agent",
|
||||||
31: 'Yima',
|
31: "Yima",
|
||||||
32: 'ikkei',
|
32: "ikkei",
|
||||||
33: 'echidna',
|
33: "echidna",
|
||||||
34: 'lithos',
|
34: "lithos",
|
||||||
35: 'SMOKE',
|
35: "SMOKE",
|
||||||
36: 'the KING',
|
36: "the KING",
|
||||||
37: 'Kicoro',
|
37: "Kicoro",
|
||||||
38: 'DEBORAH',
|
38: "DEBORAH",
|
||||||
39: 'Teruo',
|
39: "Teruo",
|
||||||
40: 'the TOWER',
|
40: "the TOWER",
|
||||||
41: 'Mamoru-kun',
|
41: "Mamoru-kun",
|
||||||
42: 'Canopus',
|
42: "Canopus",
|
||||||
43: 'Mimi Nyami',
|
43: "Mimi Nyami",
|
||||||
44: 'iO-LOWER',
|
44: "iO-LOWER",
|
||||||
45: 'BOY',
|
45: "BOY",
|
||||||
46: 'Sergei',
|
46: "Sergei",
|
||||||
47: 'SAPPHIRE',
|
47: "SAPPHIRE",
|
||||||
48: 'Chocky',
|
48: "Chocky",
|
||||||
49: 'HAPPPY',
|
49: "HAPPPY",
|
||||||
50: 'SHOLLKEE',
|
50: "SHOLLKEE",
|
||||||
51: 'CHARA-O',
|
51: "CHARA-O",
|
||||||
52: 'Hugh, GRIM, SUMIKO',
|
52: "Hugh, GRIM, SUMIKO",
|
||||||
53: 'Peetan',
|
53: "Peetan",
|
||||||
54: 'SHARK',
|
54: "SHARK",
|
||||||
55: 'Nakajima-san',
|
55: "Nakajima-san",
|
||||||
56: 'KIKYO',
|
56: "KIKYO",
|
||||||
57: 'SUMIRE',
|
57: "SUMIRE",
|
||||||
58: 'NAKAJI',
|
58: "NAKAJI",
|
||||||
59: 'moi moi',
|
59: "moi moi",
|
||||||
60: 'TITICACA',
|
60: "TITICACA",
|
||||||
61: 'MASAMUNE',
|
61: "MASAMUNE",
|
||||||
62: 'YUMMY'
|
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.
|
# 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',
|
"name": "Holiday Greeting",
|
||||||
'tip': 'Changes the payment selection confirmation sound.',
|
"tip": "Changes the payment selection confirmation sound.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'holiday_greeting',
|
"setting": "holiday_greeting",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Okay!',
|
0: "Okay!",
|
||||||
1: 'Merry Christmas!',
|
1: "Merry Christmas!",
|
||||||
2: 'Happy New Year!',
|
2: "Happy New Year!",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
# peace soundtrack hatsubai kinen SP event, 0 = off, 1 = active, 2 = off (0-2)
|
# peace soundtrack hatsubai kinen SP event, 0 = off, 1 = active, 2 = off (0-2)
|
||||||
'name': 'peace soundtrack hatsubai kinen SP',
|
"name": "peace soundtrack hatsubai kinen SP",
|
||||||
'tip': 'peace soundtrack hatsubai kinen SP for all players.',
|
"tip": "peace soundtrack hatsubai kinen SP for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'peace_soundtrack',
|
"setting": "peace_soundtrack",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Not stated',
|
0: "Not stated",
|
||||||
1: 'Active',
|
1: "Active",
|
||||||
2: 'Ended',
|
2: "Ended",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'MZD no kimagure tanteisha joshu',
|
"name": "MZD no kimagure tanteisha joshu",
|
||||||
'tip': 'Boost increasing the Clarification Level, if four or more Requests still unresolved.',
|
"tip": "Boost increasing the Clarification Level, if four or more Requests still unresolved.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'tanteisha_joshu',
|
"setting": "tanteisha_joshu",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Not stated',
|
0: "Not stated",
|
||||||
1: 'Active',
|
1: "Active",
|
||||||
2: 'Ended',
|
2: "Ended",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
# Shutchou! pop'n quest Lively II event
|
# Shutchou! pop'n quest Lively II event
|
||||||
'name': 'Shutchou! pop\'n quest Lively phase',
|
"name": "Shutchou! pop'n quest Lively phase",
|
||||||
'tip': 'Shutchou! pop\'n quest Lively phase for all players.',
|
"tip": "Shutchou! pop'n quest Lively phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'popn_quest_lively',
|
"setting": "popn_quest_lively",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Not started',
|
0: "Not started",
|
||||||
1: 'fes 1',
|
1: "fes 1",
|
||||||
2: 'fes 2',
|
2: "fes 2",
|
||||||
3: 'fes FINAL',
|
3: "fes FINAL",
|
||||||
4: 'fes EXTRA',
|
4: "fes EXTRA",
|
||||||
5: 'Ended',
|
5: "Ended",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
# Shutchou! pop'n quest Lively II event
|
# Shutchou! pop'n quest Lively II event
|
||||||
'name': 'Shutchou! pop\'n quest Lively II phase',
|
"name": "Shutchou! pop'n quest Lively II phase",
|
||||||
'tip': 'Shutchou! pop\'n quest Lively II phase for all players.',
|
"tip": "Shutchou! pop'n quest Lively II phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'popn_quest_lively_2',
|
"setting": "popn_quest_lively_2",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Not started',
|
0: "Not started",
|
||||||
1: 'fes 1',
|
1: "fes 1",
|
||||||
2: 'fes 2',
|
2: "fes 2",
|
||||||
3: 'fes FINAL',
|
3: "fes FINAL",
|
||||||
4: 'fes EXTRA',
|
4: "fes EXTRA",
|
||||||
5: 'fes THE END',
|
5: "fes THE END",
|
||||||
6: 'Ended',
|
6: "Ended",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'bools': [
|
"bools": [
|
||||||
# We don't currently support lobbies or anything, so this is commented out until
|
# We don't currently support lobbies or anything, so this is commented out until
|
||||||
# somebody gets around to implementing it.
|
# somebody gets around to implementing it.
|
||||||
# {
|
# {
|
||||||
@ -208,24 +208,24 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
|||||||
# 'setting': 'enable_net_taisen',
|
# 'setting': 'enable_net_taisen',
|
||||||
# },
|
# },
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
music_phase = game_config.get_int('music_phase')
|
music_phase = game_config.get_int("music_phase")
|
||||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||||||
mn_tanteisha_phase = game_config.get_int('mn_tanteisha_phase')
|
mn_tanteisha_phase = game_config.get_int("mn_tanteisha_phase")
|
||||||
peace_soundtrack = game_config.get_int('peace_soundtrack')
|
peace_soundtrack = game_config.get_int("peace_soundtrack")
|
||||||
tanteisha_joshu = game_config.get_int('tanteisha_joshu')
|
tanteisha_joshu = game_config.get_int("tanteisha_joshu")
|
||||||
popn_quest_lively = game_config.get_int('popn_quest_lively')
|
popn_quest_lively = game_config.get_int("popn_quest_lively")
|
||||||
popn_quest_lively_2 = game_config.get_int('popn_quest_lively_2')
|
popn_quest_lively_2 = game_config.get_int("popn_quest_lively_2")
|
||||||
|
|
||||||
# Event phases
|
# Event phases
|
||||||
return (
|
return (
|
||||||
@ -384,20 +384,31 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
|||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
root = super().format_profile(userid, profile)
|
root = super().format_profile(userid, profile)
|
||||||
|
|
||||||
account = root.child('account')
|
account = root.child("account")
|
||||||
account.add_child(Node.s16('card_again_count', profile.get_int('card_again_count')))
|
account.add_child(
|
||||||
account.add_child(Node.s16('sp_riddles_id', profile.get_int('sp_riddles_id')))
|
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
|
# Kaimei riddles events
|
||||||
event2021 = Node.void('event2021')
|
event2021 = Node.void("event2021")
|
||||||
root.add_child(event2021)
|
root.add_child(event2021)
|
||||||
event2021.add_child(Node.u32('point', profile.get_int('point')))
|
event2021.add_child(Node.u32("point", profile.get_int("point")))
|
||||||
event2021.add_child(Node.u8('step', profile.get_int('step')))
|
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(
|
||||||
event2021.add_child(Node.u8('step_nos', profile.get_int('step_nos')))
|
Node.u32_array(
|
||||||
event2021.add_child(Node.u32_array('quest_point_nos', profile.get_int_array('quest_point_nos', 13, [0] * 13)))
|
"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)
|
root.add_child(riddles_data)
|
||||||
|
|
||||||
# Generate Short Riddles for MN tanteisha
|
# Generate Short Riddles for MN tanteisha
|
||||||
@ -413,62 +424,74 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
|||||||
|
|
||||||
randomRiddles.append(riddle)
|
randomRiddles.append(riddle)
|
||||||
|
|
||||||
sh_riddles = Node.void('sh_riddles')
|
sh_riddles = Node.void("sh_riddles")
|
||||||
riddles_data.add_child(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
|
# 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:
|
for achievement in achievements:
|
||||||
if achievement.type == 'riddle':
|
if achievement.type == "riddle":
|
||||||
kaimei_gauge = achievement.data.get_int('kaimei_gauge')
|
kaimei_gauge = achievement.data.get_int("kaimei_gauge")
|
||||||
is_cleared = achievement.data.get_bool('is_cleared')
|
is_cleared = achievement.data.get_bool("is_cleared")
|
||||||
riddles_cleared = achievement.data.get_bool('riddles_cleared')
|
riddles_cleared = achievement.data.get_bool("riddles_cleared")
|
||||||
select_count = achievement.data.get_int('select_count')
|
select_count = achievement.data.get_int("select_count")
|
||||||
other_count = achievement.data.get_int('other_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)
|
riddles_data.add_child(sp_riddles)
|
||||||
sp_riddles.add_child(Node.u16('kaimei_gauge', kaimei_gauge))
|
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("is_cleared", is_cleared))
|
||||||
sp_riddles.add_child(Node.bool('riddles_cleared', riddles_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.u8("select_count", select_count))
|
||||||
sp_riddles.add_child(Node.u32('other_count', other_count))
|
sp_riddles.add_child(Node.u32("other_count", other_count))
|
||||||
|
|
||||||
return root
|
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)
|
newprofile = super().unformat_profile(userid, request, oldprofile)
|
||||||
|
|
||||||
account = request.child('account')
|
account = request.child("account")
|
||||||
if account is not None:
|
if account is not None:
|
||||||
newprofile.replace_int('card_again_count', account.child_value('card_again_count'))
|
newprofile.replace_int(
|
||||||
newprofile.replace_int('sp_riddles_id', account.child_value('sp_riddles_id'))
|
"card_again_count", account.child_value("card_again_count")
|
||||||
|
)
|
||||||
|
newprofile.replace_int(
|
||||||
|
"sp_riddles_id", account.child_value("sp_riddles_id")
|
||||||
|
)
|
||||||
|
|
||||||
# Kaimei riddles events
|
# Kaimei riddles events
|
||||||
event2021 = request.child('event2021')
|
event2021 = request.child("event2021")
|
||||||
if event2021 is not None:
|
if event2021 is not None:
|
||||||
newprofile.replace_int('point', event2021.child_value('point'))
|
newprofile.replace_int("point", event2021.child_value("point"))
|
||||||
newprofile.replace_int('step', event2021.child_value('step'))
|
newprofile.replace_int("step", event2021.child_value("step"))
|
||||||
newprofile.replace_int_array('quest_point', 8, event2021.child_value('quest_point'))
|
newprofile.replace_int_array(
|
||||||
newprofile.replace_int('step_nos', event2021.child_value('step_nos'))
|
"quest_point", 8, event2021.child_value("quest_point")
|
||||||
newprofile.replace_int_array('quest_point_nos', 13, event2021.child_value('quest_point_nos'))
|
)
|
||||||
|
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
|
# Extract kaimei riddles achievements
|
||||||
for node in request.children:
|
for node in request.children:
|
||||||
if node.name == 'riddles_data':
|
if node.name == "riddles_data":
|
||||||
riddle_id = 0
|
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:
|
for riddle in node.children:
|
||||||
kaimei_gauge = riddle.child_value('kaimei_gauge')
|
kaimei_gauge = riddle.child_value("kaimei_gauge")
|
||||||
is_cleared = riddle.child_value('is_cleared')
|
is_cleared = riddle.child_value("is_cleared")
|
||||||
riddles_cleared = riddle.child_value('riddles_cleared')
|
riddles_cleared = riddle.child_value("riddles_cleared")
|
||||||
select_count = riddle.child_value('select_count')
|
select_count = riddle.child_value("select_count")
|
||||||
other_count = riddle.child_value('other_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
|
select_count = 3
|
||||||
elif (playedRiddle == riddle_id):
|
elif playedRiddle == riddle_id:
|
||||||
select_count += 1
|
select_count += 1
|
||||||
|
|
||||||
self.data.local.user.put_achievement(
|
self.data.local.user.put_achievement(
|
||||||
@ -476,13 +499,13 @@ class PopnMusicKaimei(PopnMusicModernBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
riddle_id,
|
riddle_id,
|
||||||
'riddle',
|
"riddle",
|
||||||
{
|
{
|
||||||
'kaimei_gauge': kaimei_gauge,
|
"kaimei_gauge": kaimei_gauge,
|
||||||
'is_cleared': is_cleared,
|
"is_cleared": is_cleared,
|
||||||
'riddles_cleared': riddles_cleared,
|
"riddles_cleared": riddles_cleared,
|
||||||
'select_count': select_count,
|
"select_count": select_count,
|
||||||
'other_count': other_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 all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'ints': [
|
"ints": [
|
||||||
{
|
{
|
||||||
'name': 'Music Open Phase',
|
"name": "Music Open Phase",
|
||||||
'tip': 'Default music phase for all players.',
|
"tip": "Default music phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'music_phase',
|
"setting": "music_phase",
|
||||||
'values': {
|
"values": {
|
||||||
# The value goes to 23 now, but it starts where usaneko left off at 11
|
# The value goes to 23 now, but it starts where usaneko left off at 11
|
||||||
# Unlocks a total of 53 songs
|
# Unlocks a total of 53 songs
|
||||||
12: 'No music unlocks',
|
12: "No music unlocks",
|
||||||
13: 'Phase 1',
|
13: "Phase 1",
|
||||||
14: 'Phase 2',
|
14: "Phase 2",
|
||||||
15: 'Phase 3',
|
15: "Phase 3",
|
||||||
16: 'Phase 4',
|
16: "Phase 4",
|
||||||
17: 'Phase 5',
|
17: "Phase 5",
|
||||||
18: 'Phase 6',
|
18: "Phase 6",
|
||||||
19: 'Phase 7',
|
19: "Phase 7",
|
||||||
20: 'Phase 8',
|
20: "Phase 8",
|
||||||
21: 'Phase 9',
|
21: "Phase 9",
|
||||||
22: 'Phase 10',
|
22: "Phase 10",
|
||||||
23: 'Phase MAX',
|
23: "Phase MAX",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'NAVI-Kun Event Phase',
|
"name": "NAVI-Kun Event Phase",
|
||||||
'tip': 'NAVI-Kun event phase for all players.',
|
"tip": "NAVI-Kun event phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'navikun_phase',
|
"setting": "navikun_phase",
|
||||||
'values': {
|
"values": {
|
||||||
# The value goes to 30 now, but it starts where usaneko left off at 15
|
# The value goes to 30 now, but it starts where usaneko left off at 15
|
||||||
# Unlocks a total of 89 songs
|
# Unlocks a total of 89 songs
|
||||||
15: 'Phase 1',
|
15: "Phase 1",
|
||||||
16: 'Phase 2',
|
16: "Phase 2",
|
||||||
17: 'Phase 3',
|
17: "Phase 3",
|
||||||
18: 'Phase 4',
|
18: "Phase 4",
|
||||||
19: 'Phase 5',
|
19: "Phase 5",
|
||||||
20: 'Phase 6',
|
20: "Phase 6",
|
||||||
21: 'Phase 7',
|
21: "Phase 7",
|
||||||
22: 'Phase 8',
|
22: "Phase 8",
|
||||||
23: 'Phase 9',
|
23: "Phase 9",
|
||||||
24: 'Phase 10',
|
24: "Phase 10",
|
||||||
25: 'Phase 11',
|
25: "Phase 11",
|
||||||
26: 'Phase 12',
|
26: "Phase 12",
|
||||||
27: 'Phase 13',
|
27: "Phase 13",
|
||||||
28: 'Phase 14',
|
28: "Phase 14",
|
||||||
29: 'Phase 15',
|
29: "Phase 15",
|
||||||
30: 'Phase MAX',
|
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.
|
# 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',
|
"name": "Holiday Greeting",
|
||||||
'tip': 'Changes the payment selection confirmation sound.',
|
"tip": "Changes the payment selection confirmation sound.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'holiday_greeting',
|
"setting": "holiday_greeting",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Okay!',
|
0: "Okay!",
|
||||||
1: 'Merry Christmas!',
|
1: "Merry Christmas!",
|
||||||
2: 'Happy New Year!',
|
2: "Happy New Year!",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
# The following values control the pop'n music event archive. Setting the flag to the following values has the
|
# 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
|
# 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,
|
# 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.
|
# setting it to 20 makes all of the events available for selection. Completing the minigame unlocks the associated content.
|
||||||
'name': 'Event Archive Phase',
|
"name": "Event Archive Phase",
|
||||||
'tip': 'Event Archive mini-game phase for all players.',
|
"tip": "Event Archive mini-game phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'event_archive_phase',
|
"setting": "event_archive_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Event Archive disabled',
|
0: "Event Archive disabled",
|
||||||
1: 'pop\'n music 11 - The Latest Space Station',
|
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',
|
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',
|
3: "pop'n music Sunny Park - I Love Walking in Happiness Park",
|
||||||
4: 'pop\'n music 12 Iroha - Ninja Code: April 1st Volume',
|
4: "pop'n music 12 Iroha - Ninja Code: April 1st Volume",
|
||||||
5: 'pop\'n music 15 ADVENTURE - Route to Awaken the Soul',
|
5: "pop'n music 15 ADVENTURE - Route to Awaken the Soul",
|
||||||
6: 'pop\'n music 20 fantasia - A Braided Fantasy Song',
|
6: "pop'n music 20 fantasia - A Braided Fantasy Song",
|
||||||
7: 'EXTRA',
|
7: "EXTRA",
|
||||||
8: 'pop\'n music 15 ADVENTURE - A Route with a Faint Bell Sound',
|
8: "pop'n music 15 ADVENTURE - A Route with a Faint Bell Sound",
|
||||||
9: 'pop\'n music 13 Carnival - Bunny Magician Attraction',
|
9: "pop'n music 13 Carnival - Bunny Magician Attraction",
|
||||||
10: 'pop\'n music 14 FEVER! - That Burning Special Attack, again!',
|
10: "pop'n music 14 FEVER! - That Burning Special Attack, again!",
|
||||||
11: 'pop\'n music Sunny Park - Festival Nightfall Park',
|
11: "pop'n music Sunny Park - Festival Nightfall Park",
|
||||||
12: 'pop\'n music 20 fantasia - A Fantasy Song by the Bladed Warrior',
|
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',
|
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',
|
14: "pop'n music éclale - Fun Rag Hour",
|
||||||
15: 'pop\'n music 13 Carnival - Ghost Piano Attraction',
|
15: "pop'n music 13 Carnival - Ghost Piano Attraction",
|
||||||
16: 'pop\'n music 14 FEVER! - That Warrior Defending Peace, again!',
|
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',
|
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',
|
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',
|
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',
|
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
|
# We don't currently support lobbies or anything, so this is commented out until
|
||||||
# somebody gets around to implementing it.
|
# somebody gets around to implementing it.
|
||||||
# {
|
# {
|
||||||
@ -141,21 +141,21 @@ class PopnMusicPeace(PopnMusicModernBase):
|
|||||||
# 'setting': 'enable_net_taisen',
|
# 'setting': 'enable_net_taisen',
|
||||||
# },
|
# },
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
music_phase = game_config.get_int('music_phase')
|
music_phase = game_config.get_int("music_phase")
|
||||||
event_archive_phase = game_config.get_int('event_archive_phase')
|
event_archive_phase = game_config.get_int("event_archive_phase")
|
||||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
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
|
# Event phases
|
||||||
return (
|
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 all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'ints': [
|
"ints": [
|
||||||
{
|
{
|
||||||
'name': 'Game Phase',
|
"name": "Game Phase",
|
||||||
'tip': 'Game unlock phase for all players.',
|
"tip": "Game unlock phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'game_phase',
|
"setting": "game_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'NO PHASE',
|
0: "NO PHASE",
|
||||||
1: 'SECRET DATA RELEASE',
|
1: "SECRET DATA RELEASE",
|
||||||
2: 'MAX: ALL DATA RELEASE',
|
2: "MAX: ALL DATA RELEASE",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Town Mode Phase',
|
"name": "Town Mode Phase",
|
||||||
'tip': 'Town mode phase for all players.',
|
"tip": "Town mode phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'town_phase',
|
"setting": "town_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'town mode disabled',
|
0: "town mode disabled",
|
||||||
1: 'town phase 1',
|
1: "town phase 1",
|
||||||
2: 'town phase 2',
|
2: "town phase 2",
|
||||||
3: 'Pop\'n Naan Festival',
|
3: "Pop'n Naan Festival",
|
||||||
# 4 seems to be a continuation of town phase 2. Intentionally leaving it out.
|
# 4 seems to be a continuation of town phase 2. Intentionally leaving it out.
|
||||||
5: 'town phase 3',
|
5: "town phase 3",
|
||||||
6: 'town phase 4',
|
6: "town phase 4",
|
||||||
7: 'Miracle 4 + 1',
|
7: "Miracle 4 + 1",
|
||||||
# 8 seems to be a continuation of town phase 4. Intentionally leaving it out.
|
# 8 seems to be a continuation of town phase 4. Intentionally leaving it out.
|
||||||
9: 'town phase MAX',
|
9: "town phase MAX",
|
||||||
10: 'Find your daughter!',
|
10: "Find your daughter!",
|
||||||
# 11 is a continuation of phase MAX after find your daughter, with Tanabata
|
# 11 is a continuation of phase MAX after find your daughter, with Tanabata
|
||||||
# bamboo grass added as well.
|
# bamboo grass added as well.
|
||||||
11: 'town phase MAX+1',
|
11: "town phase MAX+1",
|
||||||
12: 'Peruri-san visits',
|
12: "Peruri-san visits",
|
||||||
# 13 is a continuation of phase MAX+1 after peruri-san visits, with Watermelon
|
# 13 is a continuation of phase MAX+1 after peruri-san visits, with Watermelon
|
||||||
# pattern tank added as well.
|
# pattern tank added as well.
|
||||||
13: 'town phase MAX+2',
|
13: "town phase MAX+2",
|
||||||
14: 'Find Deuil!',
|
14: "Find Deuil!",
|
||||||
# 15 is a continuation of phase MAX+2 after find deuil, with Tsukimi dumplings
|
# 15 is a continuation of phase MAX+2 after find deuil, with Tsukimi dumplings
|
||||||
# added as well.
|
# added as well.
|
||||||
15: 'town phase MAX+3',
|
15: "town phase MAX+3",
|
||||||
16: 'Landmark stamp rally',
|
16: "Landmark stamp rally",
|
||||||
# 17 is a continuation of MAX+3 after landmark stamp rally ends, but offering
|
# 17 is a continuation of MAX+3 after landmark stamp rally ends, but offering
|
||||||
# no additional stuff.
|
# no additional stuff.
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Customization Unlock',
|
"name": "Force Customization Unlock",
|
||||||
'tip': 'Force unlock all theme and menu customizations.',
|
"tip": "Force unlock all theme and menu customizations.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_customizations',
|
"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_DIAMOND_FULL_COMBO: self.GAME_PLAY_FLAG_FULL_COMBO,
|
||||||
self.PLAY_MEDAL_STAR_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,
|
self.PLAY_MEDAL_PERFECT: self.GAME_PLAY_FLAG_PERFECT_COMBO,
|
||||||
}[score.data.get_int('medal')]
|
}[score.data.get_int("medal")]
|
||||||
return (flags << shift) | playedflag
|
return (flags << shift) | playedflag
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
|
|
||||||
# Format profile
|
# Format profile
|
||||||
binary_profile = [0] * 2198
|
binary_profile = [0] * 2198
|
||||||
@ -178,7 +178,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
# Copy name. We intentionally leave location 12 alone as it is
|
# Copy name. We intentionally leave location 12 alone as it is
|
||||||
# the null termination for the name if it happens to be 12
|
# the null termination for the name if it happens to be 12
|
||||||
# characters (6 shift-jis kana).
|
# 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):
|
for name_pos, byte in enumerate(name_binary):
|
||||||
binary_profile[name_pos] = byte
|
binary_profile[name_pos] = byte
|
||||||
|
|
||||||
@ -199,32 +199,32 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
13: 5,
|
13: 5,
|
||||||
14: 5,
|
14: 5,
|
||||||
15: 5,
|
15: 5,
|
||||||
}[profile.get_int('play_mode')]
|
}[profile.get_int("play_mode")]
|
||||||
|
|
||||||
# Copy miscelaneous values
|
# Copy miscelaneous values
|
||||||
binary_profile[15] = profile.get_int('last_play_flag') & 0xFF
|
binary_profile[15] = profile.get_int("last_play_flag") & 0xFF
|
||||||
binary_profile[16] = profile.get_int('medal_and_friend') & 0xFF
|
binary_profile[16] = profile.get_int("medal_and_friend") & 0xFF
|
||||||
binary_profile[37] = profile.get_int('read_news') & 0xFF
|
binary_profile[37] = profile.get_int("read_news") & 0xFF
|
||||||
binary_profile[38] = profile.get_int('skin_tex_note') & 0xFF
|
binary_profile[38] = profile.get_int("skin_tex_note") & 0xFF
|
||||||
binary_profile[39] = profile.get_int('skin_tex_cmn') & 0xFF
|
binary_profile[39] = profile.get_int("skin_tex_cmn") & 0xFF
|
||||||
binary_profile[40] = profile.get_int('skin_sd_bgm') & 0xFF
|
binary_profile[40] = profile.get_int("skin_sd_bgm") & 0xFF
|
||||||
binary_profile[41] = profile.get_int('skin_sd_se') & 0xFF
|
binary_profile[41] = profile.get_int("skin_sd_se") & 0xFF
|
||||||
binary_profile[44] = profile.get_int('option') & 0xFF
|
binary_profile[44] = profile.get_int("option") & 0xFF
|
||||||
binary_profile[45] = (profile.get_int('option') >> 8) & 0xFF
|
binary_profile[45] = (profile.get_int("option") >> 8) & 0xFF
|
||||||
binary_profile[46] = (profile.get_int('option') >> 16) & 0xFF
|
binary_profile[46] = (profile.get_int("option") >> 16) & 0xFF
|
||||||
binary_profile[47] = (profile.get_int('option') >> 24) & 0xFF
|
binary_profile[47] = (profile.get_int("option") >> 24) & 0xFF
|
||||||
binary_profile[48] = profile.get_int('jubeat_collabo') & 0xFF
|
binary_profile[48] = profile.get_int("jubeat_collabo") & 0xFF
|
||||||
binary_profile[49] = (profile.get_int('jubeat_collabo') >> 8) & 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.
|
# 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[60] = profile.get_int("chara", -1) & 0xFF
|
||||||
binary_profile[61] = (profile.get_int('chara', -1) >> 8) & 0xFF
|
binary_profile[61] = (profile.get_int("chara", -1) >> 8) & 0xFF
|
||||||
binary_profile[62] = profile.get_int('music') & 0xFF
|
binary_profile[62] = profile.get_int("music") & 0xFF
|
||||||
binary_profile[63] = (profile.get_int('music') >> 8) & 0xFF
|
binary_profile[63] = (profile.get_int("music") >> 8) & 0xFF
|
||||||
binary_profile[64] = profile.get_int('sheet') & 0xFF
|
binary_profile[64] = profile.get_int("sheet") & 0xFF
|
||||||
binary_profile[65] = profile.get_int('category') & 0xFF
|
binary_profile[65] = profile.get_int("category") & 0xFF
|
||||||
binary_profile[66] = profile.get_int('norma_point') & 0xFF
|
binary_profile[66] = profile.get_int("norma_point") & 0xFF
|
||||||
binary_profile[67] = (profile.get_int('norma_point') >> 8) & 0xFF
|
binary_profile[67] = (profile.get_int("norma_point") >> 8) & 0xFF
|
||||||
|
|
||||||
# Format Scores
|
# Format Scores
|
||||||
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 7) * 17) + 7) / 8)
|
hiscore_array = [0] * int((((self.GAME_MAX_MUSIC_ID * 7) * 17) + 7) / 8)
|
||||||
@ -238,14 +238,18 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
self.CHART_TYPE_EASY,
|
self.CHART_TYPE_EASY,
|
||||||
]:
|
]:
|
||||||
continue
|
continue
|
||||||
if score.data.get_int('medal') == self.PLAY_MEDAL_NO_PLAY:
|
if score.data.get_int("medal") == self.PLAY_MEDAL_NO_PLAY:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
flags = self.__format_flags_for_score(score)
|
flags = self.__format_flags_for_score(score)
|
||||||
|
|
||||||
flags_index = score.id * 2
|
flags_index = score.id * 2
|
||||||
binary_profile[108 + flags_index] = binary_profile[108 + flags_index] | (flags & 0xFF)
|
binary_profile[108 + flags_index] = binary_profile[108 + flags_index] | (
|
||||||
binary_profile[109 + flags_index] = binary_profile[109 + flags_index] | ((flags >> 8) & 0xFF)
|
flags & 0xFF
|
||||||
|
)
|
||||||
|
binary_profile[109 + flags_index] = binary_profile[109 + flags_index] | (
|
||||||
|
(flags >> 8) & 0xFF
|
||||||
|
)
|
||||||
|
|
||||||
if score.chart in [
|
if score.chart in [
|
||||||
self.CHART_TYPE_ENJOY_5_BUTTON,
|
self.CHART_TYPE_ENJOY_5_BUTTON,
|
||||||
@ -267,12 +271,23 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
hiscore_byte_pos = int((hiscore_index * 17) / 8)
|
||||||
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
hiscore_bit_pos = int((hiscore_index * 17) % 8)
|
||||||
hiscore_value = score.points << hiscore_bit_pos
|
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] = hiscore_array[hiscore_byte_pos] | (
|
||||||
hiscore_array[hiscore_byte_pos + 1] = hiscore_array[hiscore_byte_pos + 1] | ((hiscore_value >> 8) & 0xFF)
|
hiscore_value & 0xFF
|
||||||
hiscore_array[hiscore_byte_pos + 2] = hiscore_array[hiscore_byte_pos + 2] | ((hiscore_value >> 16) & 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
|
# 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:
|
while len(most_played) < 20:
|
||||||
most_played.append(-1)
|
most_played.append(-1)
|
||||||
profile_pos = 68
|
profile_pos = 68
|
||||||
@ -291,22 +306,24 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
# - 10 appears to be purchased BGMs.
|
# - 10 appears to be purchased BGMs.
|
||||||
# - 11 appears to be purchased sound effects.
|
# - 11 appears to be purchased sound effects.
|
||||||
binary_town = [0] * 141
|
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.
|
# 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.
|
# 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[0] = town.get_int("points") & 0xFF
|
||||||
binary_town[1] = (town.get_int('points') >> 8) & 0xFF
|
binary_town[1] = (town.get_int("points") >> 8) & 0xFF
|
||||||
binary_town[2] = (town.get_int('points') >> 16) & 0xFF
|
binary_town[2] = (town.get_int("points") >> 16) & 0xFF
|
||||||
binary_town[3] = (town.get_int('points') >> 24) & 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).
|
# 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()
|
game_config = self.get_game_config()
|
||||||
force_unlock_songs = game_config.get_bool('force_unlock_songs')
|
force_unlock_songs = game_config.get_bool("force_unlock_songs")
|
||||||
force_unlock_customizations = game_config.get_bool('force_unlock_customizations')
|
force_unlock_customizations = game_config.get_bool(
|
||||||
|
"force_unlock_customizations"
|
||||||
|
)
|
||||||
|
|
||||||
if force_unlock_songs:
|
if force_unlock_songs:
|
||||||
bought_flg[0] = 0xFFFFFFFF
|
bought_flg[0] = 0xFFFFFFFF
|
||||||
@ -320,7 +337,7 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
binary_town[off + 3] = (bought_flg[flg] >> 24) & 0xFF
|
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).
|
# 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]):
|
for flg, off in enumerate([16, 20, 24, 28, 32, 36, 40, 44]):
|
||||||
binary_town[off + 0] = build_flg[flg] & 0xFF
|
binary_town[off + 0] = build_flg[flg] & 0xFF
|
||||||
binary_town[off + 1] = (build_flg[flg] >> 8) & 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
|
binary_town[off + 3] = (build_flg[flg] >> 24) & 0xFF
|
||||||
|
|
||||||
# Fill in character flags (presumably for character location, orientation, stats, etc).
|
# Fill in character flags (presumably for character location, orientation, stats, etc).
|
||||||
chara_flg = town.get_int_array('chara_flg', 19)
|
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]):
|
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 + 0] = chara_flg[flg] & 0xFF
|
||||||
binary_town[off + 1] = (chara_flg[flg] >> 8) & 0xFF
|
binary_town[off + 1] = (chara_flg[flg] >> 8) & 0xFF
|
||||||
binary_town[off + 2] = (chara_flg[flg] >> 16) & 0xFF
|
binary_town[off + 2] = (chara_flg[flg] >> 16) & 0xFF
|
||||||
binary_town[off + 3] = (chara_flg[flg] >> 24) & 0xFF
|
binary_town[off + 3] = (chara_flg[flg] >> 24) & 0xFF
|
||||||
|
|
||||||
# Fill in miscellaneous event flags.
|
# 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]):
|
for flg, off in enumerate([124, 128, 132, 136]):
|
||||||
binary_town[off + 0] = event_flg[flg] & 0xFF
|
binary_town[off + 0] = event_flg[flg] & 0xFF
|
||||||
binary_town[off + 1] = (event_flg[flg] >> 8) & 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
|
binary_town[off + 3] = (event_flg[flg] >> 24) & 0xFF
|
||||||
|
|
||||||
# Construct final profile
|
# Construct final profile
|
||||||
root.add_child(Node.binary('b', bytes(binary_profile)))
|
root.add_child(Node.binary("b", bytes(binary_profile)))
|
||||||
root.add_child(Node.binary('hiscore', bytes(hiscore_array)))
|
root.add_child(Node.binary("hiscore", bytes(hiscore_array)))
|
||||||
root.add_child(Node.binary('town', bytes(binary_town)))
|
root.add_child(Node.binary("town", bytes(binary_town)))
|
||||||
|
|
||||||
return root
|
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()
|
newprofile = oldprofile.clone()
|
||||||
|
|
||||||
# Extract the playmode, important for scores later
|
# Extract the playmode, important for scores later
|
||||||
playmode = int(request.attribute('play_mode'))
|
playmode = int(request.attribute("play_mode"))
|
||||||
newprofile.replace_int('play_mode', playmode)
|
newprofile.replace_int("play_mode", playmode)
|
||||||
|
|
||||||
# Extract profile options
|
# Extract profile options
|
||||||
newprofile.replace_int('chara', int(request.attribute('chara_num')))
|
newprofile.replace_int("chara", int(request.attribute("chara_num")))
|
||||||
if 'option' in request.attributes:
|
if "option" in request.attributes:
|
||||||
newprofile.replace_int('option', int(request.attribute('option')))
|
newprofile.replace_int("option", int(request.attribute("option")))
|
||||||
if 'last_play_flag' in request.attributes:
|
if "last_play_flag" in request.attributes:
|
||||||
newprofile.replace_int('last_play_flag', int(request.attribute('last_play_flag')))
|
newprofile.replace_int(
|
||||||
if 'medal_and_friend' in request.attributes:
|
"last_play_flag", int(request.attribute("last_play_flag"))
|
||||||
newprofile.replace_int('medal_and_friend', int(request.attribute('medal_and_friend')))
|
)
|
||||||
if 'music_num' in request.attributes:
|
if "medal_and_friend" in request.attributes:
|
||||||
newprofile.replace_int('music', int(request.attribute('music_num')))
|
newprofile.replace_int(
|
||||||
if 'sheet_num' in request.attributes:
|
"medal_and_friend", int(request.attribute("medal_and_friend"))
|
||||||
newprofile.replace_int('sheet', int(request.attribute('sheet_num')))
|
)
|
||||||
if 'category_num' in request.attributes:
|
if "music_num" in request.attributes:
|
||||||
newprofile.replace_int('category', int(request.attribute('category_num')))
|
newprofile.replace_int("music", int(request.attribute("music_num")))
|
||||||
if 'read_news_no_max' in request.attributes:
|
if "sheet_num" in request.attributes:
|
||||||
newprofile.replace_int('read_news', int(request.attribute('read_news_no_max')))
|
newprofile.replace_int("sheet", int(request.attribute("sheet_num")))
|
||||||
if 'jubeat_collabo' in request.attributes:
|
if "category_num" in request.attributes:
|
||||||
newprofile.replace_int('jubeat_collabo', int(request.attribute('jubeat_collabo')))
|
newprofile.replace_int("category", int(request.attribute("category_num")))
|
||||||
if 'norma_point' in request.attributes:
|
if "read_news_no_max" in request.attributes:
|
||||||
newprofile.replace_int('norma_point', int(request.attribute('norma_point')))
|
newprofile.replace_int(
|
||||||
if 'skin_tex_note' in request.attributes:
|
"read_news", int(request.attribute("read_news_no_max"))
|
||||||
newprofile.replace_int('skin_tex_note', int(request.attribute('skin_tex_note')))
|
)
|
||||||
if 'skin_tex_cmn' in request.attributes:
|
if "jubeat_collabo" in request.attributes:
|
||||||
newprofile.replace_int('skin_tex_cmn', int(request.attribute('skin_tex_cmn')))
|
newprofile.replace_int(
|
||||||
if 'skin_sd_bgm' in request.attributes:
|
"jubeat_collabo", int(request.attribute("jubeat_collabo"))
|
||||||
newprofile.replace_int('skin_sd_bgm', int(request.attribute('skin_sd_bgm')))
|
)
|
||||||
if 'skin_sd_se' in request.attributes:
|
if "norma_point" in request.attributes:
|
||||||
newprofile.replace_int('skin_sd_se', int(request.attribute('skin_sd_se')))
|
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
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
|
||||||
# Extract scores
|
# Extract scores
|
||||||
for node in request.children:
|
for node in request.children:
|
||||||
if node.name == 'music':
|
if node.name == "music":
|
||||||
songid = int(node.attribute('music_num'))
|
songid = int(node.attribute("music_num"))
|
||||||
chart = int(node.attribute('sheet_num'))
|
chart = int(node.attribute("sheet_num"))
|
||||||
points = int(node.attribute('score'))
|
points = int(node.attribute("score"))
|
||||||
data = int(node.attribute('data'))
|
data = int(node.attribute("data"))
|
||||||
|
|
||||||
# We never save battle scores
|
# We never save battle scores
|
||||||
if chart in [
|
if chart in [
|
||||||
@ -405,7 +458,10 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Arrange order to be compatible with future mixes
|
# 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 [
|
if chart in [
|
||||||
self.GAME_CHART_TYPE_5_BUTTON,
|
self.GAME_CHART_TYPE_5_BUTTON,
|
||||||
self.GAME_CHART_TYPE_ENJOY_5_BUTTON,
|
self.GAME_CHART_TYPE_ENJOY_5_BUTTON,
|
||||||
@ -463,26 +519,32 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
self.update_score(userid, songid, chart, points, medal)
|
self.update_score(userid, songid, chart, points, medal)
|
||||||
|
|
||||||
# Update town mode data.
|
# 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?
|
# Basic stuff that's in the base node for no reason?
|
||||||
if 'tp' in request.attributes:
|
if "tp" in request.attributes:
|
||||||
town.replace_int('points', int(request.attribute('tp')))
|
town.replace_int("points", int(request.attribute("tp")))
|
||||||
|
|
||||||
# Stuff that is in the town node
|
# Stuff that is in the town node
|
||||||
townnode = request.child('town')
|
townnode = request.child("town")
|
||||||
if townnode is not None:
|
if townnode is not None:
|
||||||
if 'play_type' in townnode.attributes:
|
if "play_type" in townnode.attributes:
|
||||||
town.replace_int('play_type', int(townnode.attribute('play_type')))
|
town.replace_int("play_type", int(townnode.attribute("play_type")))
|
||||||
if 'base' in townnode.attributes:
|
if "base" in townnode.attributes:
|
||||||
town.replace_int_array('base', 4, [int(x) for x in townnode.attribute('base').split(',')])
|
town.replace_int_array(
|
||||||
if 'bought_flg' in townnode.attributes:
|
"base", 4, [int(x) for x in townnode.attribute("base").split(",")]
|
||||||
bought_array = [int(x) for x in townnode.attribute('bought_flg').split(',')]
|
)
|
||||||
|
if "bought_flg" in townnode.attributes:
|
||||||
|
bought_array = [
|
||||||
|
int(x) for x in townnode.attribute("bought_flg").split(",")
|
||||||
|
]
|
||||||
if len(bought_array) == 3:
|
if len(bought_array) == 3:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
force_unlock_songs = game_config.get_bool('force_unlock_songs')
|
force_unlock_songs = game_config.get_bool("force_unlock_songs")
|
||||||
force_unlock_customizations = game_config.get_bool('force_unlock_customizations')
|
force_unlock_customizations = game_config.get_bool(
|
||||||
old_bought_array = town.get_int_array('bought_flg', 3)
|
"force_unlock_customizations"
|
||||||
|
)
|
||||||
|
old_bought_array = town.get_int_array("bought_flg", 3)
|
||||||
|
|
||||||
if force_unlock_songs:
|
if force_unlock_songs:
|
||||||
# Don't save force unlocked flags, it'll clobber the profile.
|
# 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.
|
# Don't save force unlocked flags, it'll clobber the profile.
|
||||||
bought_array[1] = old_bought_array[1]
|
bought_array[1] = old_bought_array[1]
|
||||||
|
|
||||||
town.replace_int_array('bought_flg', 3, bought_array)
|
town.replace_int_array("bought_flg", 3, bought_array)
|
||||||
if 'build_flg' in townnode.attributes:
|
if "build_flg" in townnode.attributes:
|
||||||
town.replace_int_array('build_flg', 8, [int(x) for x in townnode.attribute('build_flg').split(',')])
|
town.replace_int_array(
|
||||||
if 'chara_flg' in townnode.attributes:
|
"build_flg",
|
||||||
town.replace_int_array('chara_flg', 19, [int(x) for x in townnode.attribute('chara_flg').split(',')])
|
8,
|
||||||
if 'event_flg' in townnode.attributes:
|
[int(x) for x in townnode.attribute("build_flg").split(",")],
|
||||||
town.replace_int_array('event_flg', 4, [int(x) for x in townnode.attribute('event_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):
|
for bid in range(8):
|
||||||
if f'building_{bid}' in townnode.attributes:
|
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(',')])
|
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
|
return newprofile
|
||||||
|
|
||||||
def handle_game_get_request(self, request: Node) -> Node:
|
def handle_game_get_request(self, request: Node) -> Node:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
game_phase = game_config.get_int('game_phase')
|
game_phase = game_config.get_int("game_phase")
|
||||||
town_phase = game_config.get_int('town_phase')
|
town_phase = game_config.get_int("town_phase")
|
||||||
|
|
||||||
root = Node.void('game')
|
root = Node.void("game")
|
||||||
root.set_attribute('game_phase', str(game_phase)) # Phase unlocks, for song availability.
|
root.set_attribute(
|
||||||
root.set_attribute('boss_battle_point', '1')
|
"game_phase", str(game_phase)
|
||||||
root.set_attribute('boss_diff', '100,100,100,100,100,100,100,100,100,100')
|
) # Phase unlocks, for song availability.
|
||||||
root.set_attribute('card_phase', '3')
|
root.set_attribute("boss_battle_point", "1")
|
||||||
root.set_attribute('event_phase', str(town_phase)) # Town mode, for the main event.
|
root.set_attribute("boss_diff", "100,100,100,100,100,100,100,100,100,100")
|
||||||
root.set_attribute('gfdm_phase', '2')
|
root.set_attribute("card_phase", "3")
|
||||||
root.set_attribute('ir_phase', '14')
|
root.set_attribute(
|
||||||
root.set_attribute('jubeat_phase', '2')
|
"event_phase", str(town_phase)
|
||||||
root.set_attribute('local_matching_enable', '1')
|
) # Town mode, for the main event.
|
||||||
root.set_attribute('matching_sec', '120')
|
root.set_attribute("gfdm_phase", "2")
|
||||||
root.set_attribute('netvs_phase', '0') # Net taisen mode phase, maximum 18 (no lobby support).
|
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
|
return root
|
||||||
|
|
||||||
def handle_game_active_request(self, request: Node) -> Node:
|
def handle_game_active_request(self, request: Node) -> Node:
|
||||||
# Update the name of this cab for admin purposes
|
# Update the name of this cab for admin purposes
|
||||||
self.update_machine_name(request.attribute('shop_name'))
|
self.update_machine_name(request.attribute("shop_name"))
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
def handle_playerdata_expire_request(self, request: Node) -> Node:
|
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:
|
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:
|
def handle_playerdata_get_request(self, request: Node) -> Node:
|
||||||
modelstring = request.attribute('model')
|
modelstring = request.attribute("model")
|
||||||
refid = request.attribute('ref_id')
|
refid = request.attribute("ref_id")
|
||||||
root = self.get_profile_by_refid(
|
root = self.get_profile_by_refid(
|
||||||
refid,
|
refid,
|
||||||
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
self.NEW_PROFILE_ONLY if modelstring is None else self.OLD_PROFILE_ONLY,
|
||||||
)
|
)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_town_request(self, request: Node) -> Node:
|
def handle_playerdata_town_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('ref_id')
|
refid = request.attribute("ref_id")
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
|
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is None:
|
if userid is None:
|
||||||
@ -560,11 +647,11 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
if profile is None:
|
if profile is None:
|
||||||
return root
|
return root
|
||||||
|
|
||||||
town = profile.get_dict('town')
|
town = profile.get_dict("town")
|
||||||
|
|
||||||
residence = Node.void('residence')
|
residence = Node.void("residence")
|
||||||
root.add_child(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
|
# 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
|
# first one. Perhaps if there's multiple towns, the residence ID lets you choose
|
||||||
@ -572,35 +659,35 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
mapdata = [0] * 180
|
mapdata = [0] * 180
|
||||||
|
|
||||||
# Map over progress for base and buildings. Positions 173-176 are for base flags.
|
# 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):
|
for i in range(4):
|
||||||
mapdata[173 + i] = base[i]
|
mapdata[173 + i] = base[i]
|
||||||
|
|
||||||
# Positions 42-105 are for building flags.
|
# Positions 42-105 are for building flags.
|
||||||
for bid, start in enumerate([42, 50, 58, 66, 74, 82, 90, 98]):
|
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):
|
for i in range(8):
|
||||||
mapdata[start + i] = building[i]
|
mapdata[start + i] = building[i]
|
||||||
|
|
||||||
mapnode = Node.binary('map', bytes(mapdata))
|
mapnode = Node.binary("map", bytes(mapdata))
|
||||||
root.add_child(mapnode)
|
root.add_child(mapnode)
|
||||||
mapnode.set_attribute('residence', '0')
|
mapnode.set_attribute("residence", "0")
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_new_request(self, request: Node) -> Node:
|
def handle_playerdata_new_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('ref_id')
|
refid = request.attribute("ref_id")
|
||||||
name = request.attribute('name')
|
name = request.attribute("name")
|
||||||
root = self.new_profile_by_refid(refid, name)
|
root = self.new_profile_by_refid(refid, name)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('playerdata')
|
root = Node.void("playerdata")
|
||||||
root.set_attribute('status', str(Status.NO_PROFILE))
|
root.set_attribute("status", str(Status.NO_PROFILE))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_playerdata_set_request(self, request: Node) -> Node:
|
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:
|
if refid is None:
|
||||||
return root
|
return root
|
||||||
|
|
||||||
@ -608,7 +695,9 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
if userid is None:
|
if userid is None:
|
||||||
return root
|
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)
|
newprofile = self.unformat_profile(userid, request, oldprofile)
|
||||||
|
|
||||||
if newprofile is not None:
|
if newprofile is not None:
|
||||||
@ -618,4 +707,4 @@ class PopnMusicTuneStreet(PopnMusicBase):
|
|||||||
|
|
||||||
def handle_lobby_requests(self, request: Node) -> Node:
|
def handle_lobby_requests(self, request: Node) -> Node:
|
||||||
# Stub out the entire lobby service
|
# 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 all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'ints': [
|
"ints": [
|
||||||
{
|
{
|
||||||
'name': 'Music Open Phase',
|
"name": "Music Open Phase",
|
||||||
'tip': 'Default music phase for all players.',
|
"tip": "Default music phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'music_phase',
|
"setting": "music_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'No music unlocks',
|
0: "No music unlocks",
|
||||||
1: 'Phase 1',
|
1: "Phase 1",
|
||||||
2: 'Phase 2',
|
2: "Phase 2",
|
||||||
3: 'Phase 3',
|
3: "Phase 3",
|
||||||
4: 'Phase 4',
|
4: "Phase 4",
|
||||||
5: 'Phase 5',
|
5: "Phase 5",
|
||||||
6: 'Phase 6',
|
6: "Phase 6",
|
||||||
7: 'Phase 7',
|
7: "Phase 7",
|
||||||
8: 'Phase 8',
|
8: "Phase 8",
|
||||||
9: 'Phase 9',
|
9: "Phase 9",
|
||||||
10: 'Phase 10',
|
10: "Phase 10",
|
||||||
11: 'Phase MAX',
|
11: "Phase MAX",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'NAVI-Kun Event Phase',
|
"name": "NAVI-Kun Event Phase",
|
||||||
'tip': 'NAVI-Kun event phase for all players.',
|
"tip": "NAVI-Kun event phase for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'navikun_phase',
|
"setting": "navikun_phase",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Phase 1',
|
0: "Phase 1",
|
||||||
1: 'Phase 2',
|
1: "Phase 2",
|
||||||
2: 'Phase 3',
|
2: "Phase 3",
|
||||||
3: 'Phase 4',
|
3: "Phase 4",
|
||||||
4: 'Phase 5',
|
4: "Phase 5",
|
||||||
5: 'Phase 6',
|
5: "Phase 6",
|
||||||
6: 'Phase 7',
|
6: "Phase 7",
|
||||||
7: 'Phase 8',
|
7: "Phase 8",
|
||||||
8: 'Phase 9',
|
8: "Phase 9",
|
||||||
9: 'Phase 10',
|
9: "Phase 10",
|
||||||
10: 'Phase 11',
|
10: "Phase 11",
|
||||||
11: 'Phase 12',
|
11: "Phase 12",
|
||||||
12: 'Phase 13',
|
12: "Phase 13",
|
||||||
13: 'Phase 14',
|
13: "Phase 14",
|
||||||
14: 'Phase 15',
|
14: "Phase 15",
|
||||||
15: 'Phase MAX',
|
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.
|
# 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',
|
"name": "Holiday Greeting",
|
||||||
'tip': 'Changes the payment selection confirmation sound.',
|
"tip": "Changes the payment selection confirmation sound.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'holiday_greeting',
|
"setting": "holiday_greeting",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'Okay!',
|
0: "Okay!",
|
||||||
1: 'Merry Christmas!',
|
1: "Merry Christmas!",
|
||||||
2: 'Happy New Year!',
|
2: "Happy New Year!",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Active Event',
|
"name": "Active Event",
|
||||||
'tip': 'Active event for all players.',
|
"tip": "Active event for all players.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'active_event',
|
"setting": "active_event",
|
||||||
'values': {
|
"values": {
|
||||||
0: 'No event',
|
0: "No event",
|
||||||
1: 'NAVI-Kun event',
|
1: "NAVI-Kun event",
|
||||||
2: 'Daily Mission event',
|
2: "Daily Mission event",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'bools': [
|
"bools": [
|
||||||
# We don't currently support lobbies or anything, so this is commented out until
|
# We don't currently support lobbies or anything, so this is commented out until
|
||||||
# somebody gets around to implementing it.
|
# somebody gets around to implementing it.
|
||||||
# {
|
# {
|
||||||
@ -115,20 +115,20 @@ class PopnMusicUsaNeko(PopnMusicModernBase):
|
|||||||
# 'setting': 'enable_net_taisen',
|
# 'setting': 'enable_net_taisen',
|
||||||
# },
|
# },
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
def get_common_config(self) -> Tuple[Dict[int, int], bool]:
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
music_phase = game_config.get_int('music_phase')
|
music_phase = game_config.get_int("music_phase")
|
||||||
holiday_greeting = game_config.get_int('holiday_greeting')
|
holiday_greeting = game_config.get_int("holiday_greeting")
|
||||||
active_event = game_config.get_int('active_event')
|
active_event = game_config.get_int("active_event")
|
||||||
navikun_phase = game_config.get_int('navikun_phase')
|
navikun_phase = game_config.get_int("navikun_phase")
|
||||||
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
enable_net_taisen = False # game_config.get_bool('enable_net_taisen')
|
||||||
|
|
||||||
navikun_enabled = active_event == 1
|
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_NO_PLAY: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_NO_PLAY
|
||||||
CLEAR_TYPE_FAILED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
|
CLEAR_TYPE_FAILED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_FAILED
|
||||||
CLEAR_TYPE_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_CLEARED
|
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_HARD_CLEARED: Final[
|
||||||
CLEAR_TYPE_S_HARD_CLEARED: Final[int] = DBConstants.REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED
|
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 types, as saved/loaded from the DB
|
||||||
COMBO_TYPE_NONE: Final[int] = DBConstants.REFLEC_BEAT_COMBO_TYPE_NONE
|
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: 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
|
# Return the local2 and lobby2 service so that matching will work on newer
|
||||||
# Reflec Beat games.
|
# Reflec Beat games.
|
||||||
extra_services: List[str] = [
|
extra_services: List[str] = [
|
||||||
'local2',
|
"local2",
|
||||||
'lobby2',
|
"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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -54,9 +62,11 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
Base handler for a profile. Given a userid and a profile dictionary,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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.
|
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 None
|
||||||
return self.format_profile(userid, profile)
|
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.
|
Given a RefID and a request node, unformat the profile and save it.
|
||||||
"""
|
"""
|
||||||
@ -176,53 +188,66 @@ class ReflecBeatBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Update the last played time
|
# 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
|
# Replace clear type with highest value and timestamps
|
||||||
if clear_type >= scoredata.get_int('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(
|
||||||
scoredata.replace_int('best_clear_type_time', now)
|
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||||
history.replace_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
|
# Replace combo type with highest value and timestamps
|
||||||
if combo_type >= scoredata.get_int('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(
|
||||||
scoredata.replace_int('best_clear_type_time', now)
|
"combo_type", max(scoredata.get_int("combo_type"), combo_type)
|
||||||
history.replace_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
|
# Update the combo for this song
|
||||||
if combo is not None:
|
if combo is not None:
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
|
|
||||||
# Update the param for this song
|
# Update the param for this song
|
||||||
if param is not None:
|
if param is not None:
|
||||||
scoredata.replace_int('param', max(scoredata.get_int('param'), param))
|
scoredata.replace_int("param", max(scoredata.get_int("param"), param))
|
||||||
history.replace_int('param', param)
|
history.replace_int("param", param)
|
||||||
|
|
||||||
# Update the kflag for this song
|
# Update the kflag for this song
|
||||||
if kflag is not None:
|
if kflag is not None:
|
||||||
scoredata.replace_int('kflag', max(scoredata.get_int('kflag'), kflag))
|
scoredata.replace_int("kflag", max(scoredata.get_int("kflag"), kflag))
|
||||||
history.replace_int('kflag', kflag)
|
history.replace_int("kflag", kflag)
|
||||||
|
|
||||||
# Update win/lost/draw stats for this song
|
# Update win/lost/draw stats for this song
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
scoredata.replace_dict('stats', stats)
|
scoredata.replace_dict("stats", stats)
|
||||||
history.replace_dict('stats', stats)
|
history.replace_dict("stats", stats)
|
||||||
|
|
||||||
# Update the achievement rate with timestamps
|
# Update the achievement rate with timestamps
|
||||||
if achievement_rate >= scoredata.get_int('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(
|
||||||
scoredata.replace_int('best_achievement_rate_time', now)
|
"achievement_rate",
|
||||||
history.replace_int('achievement_rate', 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.
|
# 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 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 >= 0:
|
||||||
if miss_count <= scoredata.get_int('miss_count', 999999) or scoredata.get_int('miss_count') == -1:
|
if (
|
||||||
scoredata.replace_int('miss_count', min(scoredata.get_int('miss_count', 999999), miss_count))
|
miss_count <= scoredata.get_int("miss_count", 999999)
|
||||||
scoredata.replace_int('best_miss_count_time', now)
|
or scoredata.get_int("miss_count") == -1
|
||||||
history.replace_int('miss_count', miss_count)
|
):
|
||||||
|
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
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,12 +24,17 @@ class ReflecBeatFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['KBR', 'LBR', 'MBR']:
|
for gamecode in ["KBR", "LBR", "MBR"]:
|
||||||
Base.register(gamecode, ReflecBeatFactory)
|
Base.register(gamecode, ReflecBeatFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date < 2014060400:
|
if date < 2014060400:
|
||||||
return VersionConstants.REFLEC_BEAT_COLETTE
|
return VersionConstants.REFLEC_BEAT_COLETTE
|
||||||
@ -43,16 +48,16 @@ class ReflecBeatFactory(Factory):
|
|||||||
return VersionConstants.REFLEC_BEAT_REFLESIA
|
return VersionConstants.REFLEC_BEAT_REFLESIA
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'KBR':
|
if model.gamecode == "KBR":
|
||||||
return ReflecBeat(data, config, model)
|
return ReflecBeat(data, config, model)
|
||||||
if model.gamecode == 'LBR':
|
if model.gamecode == "LBR":
|
||||||
return ReflecBeatLimelight(data, config, model)
|
return ReflecBeatLimelight(data, config, model)
|
||||||
if model.gamecode == 'MBR':
|
if model.gamecode == "MBR":
|
||||||
if model.version is None:
|
if model.version is None:
|
||||||
if parentmodel is None:
|
if parentmodel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if parentmodel.gamecode not in ['KBR', 'LBR', 'MBR']:
|
if parentmodel.gamecode not in ["KBR", "LBR", "MBR"]:
|
||||||
return None
|
return None
|
||||||
parentversion = version_from_date(parentmodel.version)
|
parentversion = version_from_date(parentmodel.version)
|
||||||
if parentversion == VersionConstants.REFLEC_BEAT_COLETTE:
|
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 all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'ints': [],
|
"ints": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def __db_to_game_clear_type(self, db_clear_type: int, db_combo_type: int) -> int:
|
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
|
return self.GAME_CLEAR_TYPE_FULL_COMBO
|
||||||
|
|
||||||
raise Exception(f'Invalid db_combo_type {db_combo_type}')
|
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_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:
|
if game_clear_type == self.GAME_CLEAR_TYPE_NO_PLAY:
|
||||||
return (self.CLEAR_TYPE_NO_PLAY, self.COMBO_TYPE_NONE)
|
return (self.CLEAR_TYPE_NO_PLAY, self.COMBO_TYPE_NONE)
|
||||||
if game_clear_type == self.GAME_CLEAR_TYPE_PLAYED:
|
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:
|
if game_clear_type == self.GAME_CLEAR_TYPE_FULL_COMBO:
|
||||||
return (self.CLEAR_TYPE_CLEARED, self.COMBO_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:
|
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:
|
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:
|
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:
|
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)
|
machine = self.get_machine_by_id(shop_id)
|
||||||
if machine is not None:
|
if machine is not None:
|
||||||
machine_name = machine.name
|
machine_name = machine.name
|
||||||
close = machine.data.get_bool('close')
|
close = machine.data.get_bool("close")
|
||||||
hour = machine.data.get_int('hour')
|
hour = machine.data.get_int("hour")
|
||||||
minute = machine.data.get_int('minute')
|
minute = machine.data.get_int("minute")
|
||||||
pref = machine.data.get_int('pref', self.get_machine_region())
|
pref = machine.data.get_int("pref", self.get_machine_region())
|
||||||
else:
|
else:
|
||||||
machine_name = ''
|
machine_name = ""
|
||||||
close = False
|
close = False
|
||||||
hour = 0
|
hour = 0
|
||||||
minute = 0
|
minute = 0
|
||||||
pref = self.get_machine_region()
|
pref = self.get_machine_region()
|
||||||
|
|
||||||
root = Node.void('pcbinfo')
|
root = Node.void("pcbinfo")
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
root.add_child(info)
|
root.add_child(info)
|
||||||
|
|
||||||
info.add_child(Node.string('name', machine_name))
|
info.add_child(Node.string("name", machine_name))
|
||||||
info.add_child(Node.s16('pref', pref))
|
info.add_child(Node.s16("pref", pref))
|
||||||
info.add_child(Node.bool('close', close))
|
info.add_child(Node.bool("close", close))
|
||||||
info.add_child(Node.u8('hour', hour))
|
info.add_child(Node.u8("hour", hour))
|
||||||
info.add_child(Node.u8('min', minute))
|
info.add_child(Node.u8("min", minute))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_pcbinfo_set_request(self, request: Node) -> Node:
|
def handle_pcbinfo_set_request(self, request: Node) -> Node:
|
||||||
self.update_machine_name(request.child_value('info/name'))
|
self.update_machine_name(request.child_value("info/name"))
|
||||||
self.update_machine_data({
|
self.update_machine_data(
|
||||||
'close': request.child_value('info/close'),
|
{
|
||||||
'hour': request.child_value('info/hour'),
|
"close": request.child_value("info/close"),
|
||||||
'minute': request.child_value('info/min'),
|
"hour": request.child_value("info/hour"),
|
||||||
'pref': request.child_value('info/pref'),
|
"minute": request.child_value("info/min"),
|
||||||
})
|
"pref": request.child_value("info/pref"),
|
||||||
return Node.void('pcbinfo')
|
}
|
||||||
|
)
|
||||||
|
return Node.void("pcbinfo")
|
||||||
|
|
||||||
def __add_event_info(self, request: Node) -> None:
|
def __add_event_info(self, request: Node) -> None:
|
||||||
events: Dict[int, int] = {}
|
events: Dict[int, int] = {}
|
||||||
|
|
||||||
for (_eventid, _phase) in events.items():
|
for (_eventid, _phase) in events.items():
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
request.add_child(data)
|
request.add_child(data)
|
||||||
data.add_child(Node.s32('type', -1))
|
data.add_child(Node.s32("type", -1))
|
||||||
data.add_child(Node.s32('value', -1))
|
data.add_child(Node.s32("value", -1))
|
||||||
|
|
||||||
def handle_sysinfo_get_request(self, request: Node) -> Node:
|
def handle_sysinfo_get_request(self, request: Node) -> Node:
|
||||||
root = Node.void('sysinfo')
|
root = Node.void("sysinfo")
|
||||||
trd = Node.void('trd')
|
trd = Node.void("trd")
|
||||||
root.add_child(trd)
|
root.add_child(trd)
|
||||||
|
|
||||||
# Add event info
|
# Add event info
|
||||||
@ -142,16 +146,16 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_sysinfo_fan_request(self, request: Node) -> Node:
|
def handle_sysinfo_fan_request(self, request: Node) -> Node:
|
||||||
sysinfo = Node.void('sysinfo')
|
sysinfo = Node.void("sysinfo")
|
||||||
sysinfo.add_child(Node.u8('pref', self.get_machine_region()))
|
sysinfo.add_child(Node.u8("pref", self.get_machine_region()))
|
||||||
sysinfo.add_child(Node.string('lid', request.child_value('lid')))
|
sysinfo.add_child(Node.string("lid", request.child_value("lid")))
|
||||||
return sysinfo
|
return sysinfo
|
||||||
|
|
||||||
def handle_lobby_entry_request(self, request: Node) -> Node:
|
def handle_lobby_entry_request(self, request: Node) -> Node:
|
||||||
root = Node.void('lobby')
|
root = Node.void("lobby")
|
||||||
|
|
||||||
# Create a lobby entry for this user
|
# 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)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
profile = self.get_profile(userid)
|
profile = self.get_profile(userid)
|
||||||
@ -160,49 +164,49 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
{
|
{
|
||||||
'mid': request.child_value('e/mid'),
|
"mid": request.child_value("e/mid"),
|
||||||
'ng': request.child_value('e/ng'),
|
"ng": request.child_value("e/ng"),
|
||||||
'lid': request.child_value('e/lid'),
|
"lid": request.child_value("e/lid"),
|
||||||
'sn': request.child_value('e/sn'),
|
"sn": request.child_value("e/sn"),
|
||||||
'pref': request.child_value('e/pref'),
|
"pref": request.child_value("e/pref"),
|
||||||
'ga': request.child_value('e/ga'),
|
"ga": request.child_value("e/ga"),
|
||||||
'gp': request.child_value('e/gp'),
|
"gp": request.child_value("e/gp"),
|
||||||
'la': request.child_value('e/la'),
|
"la": request.child_value("e/la"),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
lobby = self.data.local.lobby.get_lobby(
|
lobby = self.data.local.lobby.get_lobby(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
)
|
)
|
||||||
root.add_child(Node.s32('eid', lobby.get_int('id')))
|
root.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e = Node.void('e')
|
e = Node.void("e")
|
||||||
root.add_child(e)
|
root.add_child(e)
|
||||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||||
e.add_child(Node.s32('uid', profile.extid))
|
e.add_child(Node.s32("uid", profile.extid))
|
||||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||||
e.add_child(Node.s32('exp', profile.get_int('exp')))
|
e.add_child(Node.s32("exp", profile.get_int("exp")))
|
||||||
e.add_child(Node.u8('mg', profile.get_int('mg')))
|
e.add_child(Node.u8("mg", profile.get_int("mg")))
|
||||||
e.add_child(Node.s32('tid', lobby.get_int('tid')))
|
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("tn", lobby.get_str("tn")))
|
||||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
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("pref", lobby.get_int("pref")))
|
||||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
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.u16("gp", lobby.get_int("gp")))
|
||||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_lobby_read_request(self, request: Node) -> Node:
|
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
|
# Look up all lobbies matching the criteria specified
|
||||||
mg = request.child_value('m_grade') # noqa: F841
|
mg = request.child_value("m_grade") # noqa: F841
|
||||||
extid = request.child_value('uid')
|
extid = request.child_value("uid")
|
||||||
limit = request.child_value('max')
|
limit = request.child_value("max")
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version)
|
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
|
# No profile info, don't return this lobby
|
||||||
continue
|
continue
|
||||||
|
|
||||||
e = Node.void('e')
|
e = Node.void("e")
|
||||||
root.add_child(e)
|
root.add_child(e)
|
||||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||||
e.add_child(Node.s32('uid', profile.extid))
|
e.add_child(Node.s32("uid", profile.extid))
|
||||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||||
e.add_child(Node.s32('exp', profile.get_int('exp')))
|
e.add_child(Node.s32("exp", profile.get_int("exp")))
|
||||||
e.add_child(Node.u8('mg', profile.get_int('mg')))
|
e.add_child(Node.u8("mg", profile.get_int("mg")))
|
||||||
e.add_child(Node.s32('tid', lobby.get_int('tid')))
|
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("tn", lobby.get_str("tn")))
|
||||||
e.add_child(Node.string('lid', lobby.get_str('lid')))
|
e.add_child(Node.string("lid", lobby.get_str("lid")))
|
||||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
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("pref", lobby.get_int("pref")))
|
||||||
e.add_child(Node.u8_array('ga', lobby.get_int_array('ga', 4)))
|
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.u16("gp", lobby.get_int("gp")))
|
||||||
e.add_child(Node.u8_array('la', lobby.get_int_array('la', 4)))
|
e.add_child(Node.u8_array("la", lobby.get_int_array("la", 4)))
|
||||||
|
|
||||||
limit = limit - 1
|
limit = limit - 1
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_lobby_delete_request(self, request: Node) -> Node:
|
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)
|
self.data.local.lobby.destroy_lobby(eid)
|
||||||
return Node.void('lobby')
|
return Node.void("lobby")
|
||||||
|
|
||||||
def handle_player_start_request(self, request: Node) -> Node:
|
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
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
self.data.local.lobby.put_play_session_info(
|
self.data.local.lobby.put_play_session_info(
|
||||||
self.game,
|
self.game, self.version, userid, {}
|
||||||
self.version,
|
|
||||||
userid,
|
|
||||||
{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
root = Node.void('player')
|
root = Node.void("player")
|
||||||
root.add_child(Node.bool('is_suc', True))
|
root.add_child(Node.bool("is_suc", True))
|
||||||
|
|
||||||
# Add event info
|
# Add event info
|
||||||
self.__add_event_info(root)
|
self.__add_event_info(root)
|
||||||
@ -267,11 +268,11 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_player_delete_request(self, request: Node) -> Node:
|
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:
|
def handle_player_end_request(self, request: Node) -> Node:
|
||||||
# Destroy play session based on info from the request
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
# Kill any lingering lobbies by this user
|
# Kill any lingering lobbies by this user
|
||||||
@ -281,159 +282,182 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
userid,
|
userid,
|
||||||
)
|
)
|
||||||
if lobby is not None:
|
if lobby is not None:
|
||||||
self.data.local.lobby.destroy_lobby(lobby.get_int('id'))
|
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_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:
|
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)
|
profile = self.get_profile_by_refid(refid)
|
||||||
if profile:
|
if profile:
|
||||||
return profile
|
return profile
|
||||||
return Node.void('player')
|
return Node.void("player")
|
||||||
|
|
||||||
def handle_player_write_request(self, request: Node) -> Node:
|
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)
|
profile = self.put_profile_by_refid(refid, request)
|
||||||
root = Node.void('player')
|
root = Node.void("player")
|
||||||
|
|
||||||
if profile is None:
|
if profile is None:
|
||||||
root.add_child(Node.s32('uid', 0))
|
root.add_child(Node.s32("uid", 0))
|
||||||
else:
|
else:
|
||||||
root.add_child(Node.s32('uid', profile.extid))
|
root.add_child(Node.s32("uid", profile.extid))
|
||||||
root.add_child(Node.s32('time', Time.now()))
|
root.add_child(Node.s32("time", Time.now()))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
statistics = self.get_play_statistics(userid)
|
statistics = self.get_play_statistics(userid)
|
||||||
game_config = self.get_game_config()
|
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)
|
scores = self.data.remote.music.get_scores(self.game, self.version, userid)
|
||||||
root = Node.void('player')
|
root = Node.void("player")
|
||||||
pdata = Node.void('pdata')
|
pdata = Node.void("pdata")
|
||||||
root.add_child(pdata)
|
root.add_child(pdata)
|
||||||
|
|
||||||
base = Node.void('base')
|
base = Node.void("base")
|
||||||
pdata.add_child(base)
|
pdata.add_child(base)
|
||||||
base.add_child(Node.s32('uid', profile.extid))
|
base.add_child(Node.s32("uid", profile.extid))
|
||||||
base.add_child(Node.string('name', profile.get_str('name')))
|
base.add_child(Node.string("name", profile.get_str("name")))
|
||||||
base.add_child(Node.s16('lv', profile.get_int('lvl')))
|
base.add_child(Node.s16("lv", profile.get_int("lvl")))
|
||||||
base.add_child(Node.s32('exp', profile.get_int('exp')))
|
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("mg", profile.get_int("mg")))
|
||||||
base.add_child(Node.s16('ap', profile.get_int('ap')))
|
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("flag", profile.get_int("flag")))
|
||||||
|
|
||||||
con = Node.void('con')
|
con = Node.void("con")
|
||||||
pdata.add_child(con)
|
pdata.add_child(con)
|
||||||
con.add_child(Node.s32('day', statistics.today_plays))
|
con.add_child(Node.s32("day", statistics.today_plays))
|
||||||
con.add_child(Node.s32('cnt', statistics.total_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("last", statistics.last_play_timestamp))
|
||||||
con.add_child(Node.s32('now', Time.now()))
|
con.add_child(Node.s32("now", Time.now()))
|
||||||
|
|
||||||
team = Node.void('team')
|
team = Node.void("team")
|
||||||
pdata.add_child(team)
|
pdata.add_child(team)
|
||||||
team.add_child(Node.s32('id', -1))
|
team.add_child(Node.s32("id", -1))
|
||||||
team.add_child(Node.string('name', ''))
|
team.add_child(Node.string("name", ""))
|
||||||
|
|
||||||
custom = Node.void('custom')
|
custom = Node.void("custom")
|
||||||
customdict = profile.get_dict('custom')
|
customdict = profile.get_dict("custom")
|
||||||
pdata.add_child(custom)
|
pdata.add_child(custom)
|
||||||
custom.add_child(Node.u8('bgm_m', customdict.get_int('bgm_m')))
|
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_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", customdict.get_int("st_bg")))
|
||||||
custom.add_child(Node.u8('st_bg_b', customdict.get_int('st_bg_b')))
|
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("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", customdict.get_int("se_s")))
|
||||||
custom.add_child(Node.u8('se_s_v', customdict.get_int('se_s_v')))
|
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)
|
pdata.add_child(released)
|
||||||
|
|
||||||
for item in achievements:
|
for item in achievements:
|
||||||
if item.type[:5] != 'item_':
|
if item.type[:5] != "item_":
|
||||||
continue
|
continue
|
||||||
itemtype = int(item.type[5:])
|
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
|
# Don't echo unlocks when we're force unlocking, we'll do it later
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
released.add_child(info)
|
released.add_child(info)
|
||||||
info.add_child(Node.u8('type', itemtype))
|
info.add_child(Node.u8("type", itemtype))
|
||||||
info.add_child(Node.u16('id', item.id))
|
info.add_child(Node.u16("id", item.id))
|
||||||
|
|
||||||
if game_config.get_bool('force_unlock_songs'):
|
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)}
|
songs = {
|
||||||
|
song.id
|
||||||
|
for song in self.data.local.music.get_all_songs(self.game, self.version)
|
||||||
|
}
|
||||||
|
|
||||||
for songid in songs:
|
for songid in songs:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
released.add_child(info)
|
released.add_child(info)
|
||||||
info.add_child(Node.u8('type', 0))
|
info.add_child(Node.u8("type", 0))
|
||||||
info.add_child(Node.u16('id', songid))
|
info.add_child(Node.u16("id", songid))
|
||||||
|
|
||||||
# Scores
|
# Scores
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
pdata.add_child(record)
|
pdata.add_child(record)
|
||||||
|
|
||||||
for score in scores:
|
for score in scores:
|
||||||
rec = Node.void('rec')
|
rec = Node.void("rec")
|
||||||
record.add_child(rec)
|
record.add_child(rec)
|
||||||
rec.add_child(Node.u16('mid', score.id))
|
rec.add_child(Node.u16("mid", score.id))
|
||||||
rec.add_child(Node.u8('ng', score.chart))
|
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("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(
|
||||||
rec.add_child(Node.s32('draw', score.data.get_dict('stats').get_int('draw')))
|
Node.s32("lose", score.data.get_dict("stats").get_int("lose"))
|
||||||
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(
|
||||||
rec.add_child(Node.s16('bs', score.points))
|
Node.s32("draw", score.data.get_dict("stats").get_int("draw"))
|
||||||
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.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.
|
# 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.
|
# 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)
|
pdata.add_child(blog)
|
||||||
|
|
||||||
# Comment (seems unused?)
|
# Comment (seems unused?)
|
||||||
pdata.add_child(Node.string('cmnt', ''))
|
pdata.add_child(Node.string("cmnt", ""))
|
||||||
|
|
||||||
return root
|
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()
|
game_config = self.get_game_config()
|
||||||
newprofile = oldprofile.clone()
|
newprofile = oldprofile.clone()
|
||||||
|
|
||||||
newprofile.replace_int('lid', ID.parse_machine_id(request.child_value('lid')))
|
newprofile.replace_int("lid", ID.parse_machine_id(request.child_value("lid")))
|
||||||
newprofile.replace_str('name', request.child_value('pdata/base/name'))
|
newprofile.replace_str("name", request.child_value("pdata/base/name"))
|
||||||
newprofile.replace_int('lvl', request.child_value('pdata/base/lv'))
|
newprofile.replace_int("lvl", request.child_value("pdata/base/lv"))
|
||||||
newprofile.replace_int('exp', request.child_value('pdata/base/exp'))
|
newprofile.replace_int("exp", request.child_value("pdata/base/exp"))
|
||||||
newprofile.replace_int('mg', request.child_value('pdata/base/mg'))
|
newprofile.replace_int("mg", request.child_value("pdata/base/mg"))
|
||||||
newprofile.replace_int('ap', request.child_value('pdata/base/ap'))
|
newprofile.replace_int("ap", request.child_value("pdata/base/ap"))
|
||||||
newprofile.replace_int('flag', request.child_value('pdata/base/flag'))
|
newprofile.replace_int("flag", request.child_value("pdata/base/flag"))
|
||||||
|
|
||||||
customdict = newprofile.get_dict('custom')
|
customdict = newprofile.get_dict("custom")
|
||||||
custom = request.child('pdata/custom')
|
custom = request.child("pdata/custom")
|
||||||
if custom:
|
if custom:
|
||||||
customdict.replace_int('bgm_m', custom.child_value('bgm_m'))
|
customdict.replace_int("bgm_m", custom.child_value("bgm_m"))
|
||||||
customdict.replace_int('st_f', custom.child_value('st_f'))
|
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", custom.child_value("st_bg"))
|
||||||
customdict.replace_int('st_bg_b', custom.child_value('st_bg_b'))
|
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("eff_e", custom.child_value("eff_e"))
|
||||||
customdict.replace_int('se_s', custom.child_value('se_s'))
|
customdict.replace_int("se_s", custom.child_value("se_s"))
|
||||||
customdict.replace_int('se_s_v', custom.child_value('se_s_v'))
|
customdict.replace_int("se_s_v", custom.child_value("se_s_v"))
|
||||||
newprofile.replace_dict('custom', customdict)
|
newprofile.replace_dict("custom", customdict)
|
||||||
|
|
||||||
# Music unlocks and other stuff
|
# Music unlocks and other stuff
|
||||||
released = request.child('pdata/released')
|
released = request.child("pdata/released")
|
||||||
if released:
|
if released:
|
||||||
for child in released.children:
|
for child in released.children:
|
||||||
if child.name != 'info':
|
if child.name != "info":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_id = child.child_value('id')
|
item_id = child.child_value("id")
|
||||||
item_type = child.child_value('type')
|
item_type = child.child_value("type")
|
||||||
if game_config.get_bool('force_unlock_songs') and item_type == 0:
|
if game_config.get_bool("force_unlock_songs") and item_type == 0:
|
||||||
# Don't save unlocks when we're force unlocking
|
# Don't save unlocks when we're force unlocking
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -442,7 +466,7 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
item_id,
|
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,
|
# the previous try. So, we must also look at the battle log for the actual play scores,
|
||||||
# and combine the data if we can.
|
# and combine the data if we can.
|
||||||
savedrecords: Dict[int, Dict[int, Dict[str, int]]] = {}
|
savedrecords: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||||
songplays = request.child('pdata/record')
|
songplays = request.child("pdata/record")
|
||||||
if songplays:
|
if songplays:
|
||||||
for child in songplays.children:
|
for child in songplays.children:
|
||||||
if child.name != 'rec':
|
if child.name != "rec":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
songid = child.child_value('mid')
|
songid = child.child_value("mid")
|
||||||
chart = child.child_value('ng')
|
chart = child.child_value("ng")
|
||||||
|
|
||||||
# These don't get sent with the battle logs, so we try to construct
|
# These don't get sent with the battle logs, so we try to construct
|
||||||
# the values here.
|
# the values here.
|
||||||
if songid not in savedrecords:
|
if songid not in savedrecords:
|
||||||
savedrecords[songid] = {}
|
savedrecords[songid] = {}
|
||||||
savedrecords[songid][chart] = {
|
savedrecords[songid][chart] = {
|
||||||
'achievement_rate': child.child_value('ar') * 10,
|
"achievement_rate": child.child_value("ar") * 10,
|
||||||
'points': child.child_value('bs'),
|
"points": child.child_value("bs"),
|
||||||
'combo': child.child_value('mc'),
|
"combo": child.child_value("mc"),
|
||||||
'miss_count': child.child_value('bmc'),
|
"miss_count": child.child_value("bmc"),
|
||||||
'win': child.child_value('win'),
|
"win": child.child_value("win"),
|
||||||
'lose': child.child_value('lose'),
|
"lose": child.child_value("lose"),
|
||||||
'draw': child.child_value('draw'),
|
"draw": child.child_value("draw"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Now, see the actual battles that were played. If we can, unify the data with a record.
|
# 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
|
# 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.
|
# 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:
|
if battlelogs:
|
||||||
for child in battlelogs.children:
|
for child in battlelogs.children:
|
||||||
if child.name != 'log':
|
if child.name != "log":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
songid = child.child_value('mid')
|
songid = child.child_value("mid")
|
||||||
chart = child.child_value('ng')
|
chart = child.child_value("ng")
|
||||||
|
|
||||||
clear_type = child.child_value('myself/ct')
|
clear_type = child.child_value("myself/ct")
|
||||||
achievement_rate = child.child_value('myself/ar') * 10
|
achievement_rate = child.child_value("myself/ar") * 10
|
||||||
points = child.child_value('myself/s')
|
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
|
combo = None
|
||||||
miss_count = -1
|
miss_count = -1
|
||||||
@ -503,17 +529,17 @@ class ReflecBeat(ReflecBeatBase):
|
|||||||
data = savedrecords[songid][chart]
|
data = savedrecords[songid][chart]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data['achievement_rate'] == achievement_rate and
|
data["achievement_rate"] == achievement_rate
|
||||||
data['points'] == points
|
and data["points"] == points
|
||||||
):
|
):
|
||||||
# This is the same record! Use the stats from it to update our
|
# This is the same record! Use the stats from it to update our
|
||||||
# internal representation.
|
# internal representation.
|
||||||
combo = data['combo']
|
combo = data["combo"]
|
||||||
miss_count = data['miss_count']
|
miss_count = data["miss_count"]
|
||||||
stats = {
|
stats = {
|
||||||
'win': data['win'],
|
"win": data["win"],
|
||||||
'lose': data['lose'],
|
"lose": data["lose"],
|
||||||
'draw': data['draw'],
|
"draw": data["draw"],
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_score(
|
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
|
return self.COMBO_TYPE_FULL_COMBO
|
||||||
if game_combo == self.GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST:
|
if game_combo == self.GAME_COMBO_TYPE_FULL_COMBO_ALL_JUST:
|
||||||
return self.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:
|
def _add_event_info(self, root: Node) -> None:
|
||||||
# Overridden in subclasses
|
# Overridden in subclasses
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _add_shop_score(self, root: Node) -> None:
|
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)
|
root.add_child(shop_score)
|
||||||
today = Node.void('today')
|
today = Node.void("today")
|
||||||
shop_score.add_child(today)
|
shop_score.add_child(today)
|
||||||
yesterday = Node.void('yesterday')
|
yesterday = Node.void("yesterday")
|
||||||
shop_score.add_child(yesterday)
|
shop_score.add_child(yesterday)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
machine = self.data.local.machine.get_machine(self.config.machine.pcbid)
|
||||||
if machine.arcade is not None:
|
if machine.arcade is not None:
|
||||||
lids = [
|
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:
|
else:
|
||||||
lids = [machine.id]
|
lids = [machine.id]
|
||||||
|
|
||||||
relevant_profiles = [
|
relevant_profiles = [
|
||||||
profile for profile in all_profiles
|
profile for profile in all_profiles if profile[1].get_int("lid", -1) in lids
|
||||||
if profile[1].get_int('lid', -1) in lids
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for (rootnode, timeoffset) in [
|
for (rootnode, timeoffset) in [
|
||||||
@ -101,10 +105,11 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
]:
|
]:
|
||||||
# Grab all attempts made in the relevant day
|
# Grab all attempts made in the relevant day
|
||||||
relevant_attempts = [
|
relevant_attempts = [
|
||||||
attempt for attempt in all_attempts
|
attempt
|
||||||
|
for attempt in all_attempts
|
||||||
if (
|
if (
|
||||||
attempt[1].timestamp >= (Time.beginning_of_today() - timeoffset) and
|
attempt[1].timestamp >= (Time.beginning_of_today() - timeoffset)
|
||||||
attempt[1].timestamp <= (Time.end_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
|
scores_by_user[userid][attempt.id][attempt.chart] = attempt
|
||||||
else:
|
else:
|
||||||
# If this attempt is better than the stored one, replace it
|
# 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
|
scores_by_user[userid][attempt.id][attempt.chart] = attempt
|
||||||
|
|
||||||
# Calculate points earned by user in the day
|
# Calculate points earned by user in the day
|
||||||
@ -129,52 +137,65 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
points_by_user[userid] = 0
|
points_by_user[userid] = 0
|
||||||
for mid in scores_by_user[userid]:
|
for mid in scores_by_user[userid]:
|
||||||
for chart in scores_by_user[userid][mid]:
|
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
|
# Output that day's earned points
|
||||||
for (userid, profile) in relevant_profiles:
|
for (userid, profile) in relevant_profiles:
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
rootnode.add_child(data)
|
rootnode.add_child(data)
|
||||||
data.add_child(Node.s16('day_id', int((Time.now() - timeoffset) / Time.SECONDS_IN_DAY)))
|
data.add_child(
|
||||||
data.add_child(Node.s32('user_id', profile.extid))
|
Node.s16(
|
||||||
data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id')))
|
"day_id", int((Time.now() - timeoffset) / Time.SECONDS_IN_DAY)
|
||||||
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.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:
|
def handle_info_rb5_info_read_request(self, request: Node) -> Node:
|
||||||
root = Node.void('info')
|
root = Node.void("info")
|
||||||
self._add_event_info(root)
|
self._add_event_info(root)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_info_rb5_info_read_hit_chart_request(self, request: Node) -> Node:
|
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 = Node.void("info")
|
||||||
root.add_child(Node.s32('ver', version))
|
root.add_child(Node.s32("ver", version))
|
||||||
ranking = Node.void('ranking')
|
ranking = Node.void("ranking")
|
||||||
root.add_child(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)
|
base = Node.void(name)
|
||||||
ranking.add_child(base)
|
ranking.add_child(base)
|
||||||
base.add_child(Node.s32('bt', start))
|
base.add_child(Node.s32("bt", start))
|
||||||
base.add_child(Node.s32('et', end))
|
base.add_child(Node.s32("et", end))
|
||||||
new = Node.void('new')
|
new = Node.void("new")
|
||||||
base.add_child(new)
|
base.add_child(new)
|
||||||
|
|
||||||
for (mid, plays) in hitchart:
|
for (mid, plays) in hitchart:
|
||||||
d = Node.void('d')
|
d = Node.void("d")
|
||||||
new.add_child(d)
|
new.add_child(d)
|
||||||
d.add_child(Node.s16('mid', mid))
|
d.add_child(Node.s16("mid", mid))
|
||||||
d.add_child(Node.s32('cnt', plays))
|
d.add_child(Node.s32("cnt", plays))
|
||||||
|
|
||||||
# Weekly hit chart
|
# Weekly hit chart
|
||||||
add_hitchart(
|
add_hitchart(
|
||||||
'weekly',
|
"weekly",
|
||||||
Time.now() - Time.SECONDS_IN_WEEK,
|
Time.now() - Time.SECONDS_IN_WEEK,
|
||||||
Time.now(),
|
Time.now(),
|
||||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 7),
|
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 7),
|
||||||
@ -182,7 +203,7 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
|
|
||||||
# Monthly hit chart
|
# Monthly hit chart
|
||||||
add_hitchart(
|
add_hitchart(
|
||||||
'monthly',
|
"monthly",
|
||||||
Time.now() - Time.SECONDS_IN_DAY * 30,
|
Time.now() - Time.SECONDS_IN_DAY * 30,
|
||||||
Time.now(),
|
Time.now(),
|
||||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 30),
|
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 30),
|
||||||
@ -190,7 +211,7 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
|
|
||||||
# All time hit chart
|
# All time hit chart
|
||||||
add_hitchart(
|
add_hitchart(
|
||||||
'total',
|
"total",
|
||||||
Time.now() - Time.SECONDS_IN_DAY * 365,
|
Time.now() - Time.SECONDS_IN_DAY * 365,
|
||||||
Time.now(),
|
Time.now(),
|
||||||
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 365),
|
self.data.local.music.get_hit_chart(self.game, self.version, 1024, 365),
|
||||||
@ -199,13 +220,13 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_info_rb5_info_read_shop_ranking_request(self, request: Node) -> Node:
|
def handle_info_rb5_info_read_shop_ranking_request(self, request: Node) -> Node:
|
||||||
start_music_id = request.child_value('min')
|
start_music_id = request.child_value("min")
|
||||||
end_music_id = request.child_value('max')
|
end_music_id = request.child_value("max")
|
||||||
|
|
||||||
root = Node.void('info')
|
root = Node.void("info")
|
||||||
shop_score = Node.void('shop_score')
|
shop_score = Node.void("shop_score")
|
||||||
root.add_child(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] = {}
|
profiles: Dict[UserID, Profile] = {}
|
||||||
for songid in range(start_music_id, end_music_id + 1):
|
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)
|
profiles[userid] = self.get_any_profile(userid)
|
||||||
profile = profiles[userid]
|
profile = profiles[userid]
|
||||||
|
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
shop_score.add_child(data)
|
shop_score.add_child(data)
|
||||||
data.add_child(Node.s32('rank', i + 1))
|
data.add_child(Node.s32("rank", i + 1))
|
||||||
data.add_child(Node.s16('music_id', songid))
|
data.add_child(Node.s16("music_id", songid))
|
||||||
data.add_child(Node.s8('note_grade', score.chart))
|
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(
|
||||||
data.add_child(Node.s32('user_id', profile.extid))
|
Node.s8(
|
||||||
data.add_child(Node.s16('icon_id', profile.get_dict('config').get_int('icon_id')))
|
"clear_type",
|
||||||
data.add_child(Node.s32('score', score.points))
|
self._db_to_game_clear_type(
|
||||||
data.add_child(Node.s32('time', score.timestamp))
|
score.data.get_int("clear_type")
|
||||||
data.add_child(Node.string('name', profile.get_str('name')))
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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
|
return root
|
||||||
|
|
||||||
def handle_lobby_rb5_lobby_entry_request(self, request: Node) -> Node:
|
def handle_lobby_rb5_lobby_entry_request(self, request: Node) -> Node:
|
||||||
root = Node.void('lobby')
|
root = Node.void("lobby")
|
||||||
root.add_child(Node.s32('interval', 120))
|
root.add_child(Node.s32("interval", 120))
|
||||||
root.add_child(Node.s32('interval_p', 120))
|
root.add_child(Node.s32("interval_p", 120))
|
||||||
|
|
||||||
# Create a lobby entry for this user
|
# 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)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
profile = self.get_profile(userid)
|
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:
|
if profile is None or info is None:
|
||||||
return root
|
return root
|
||||||
|
|
||||||
@ -266,61 +300,61 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
{
|
{
|
||||||
'mid': request.child_value('e/mid'),
|
"mid": request.child_value("e/mid"),
|
||||||
'ng': request.child_value('e/ng'),
|
"ng": request.child_value("e/ng"),
|
||||||
'mopt': request.child_value('e/mopt'),
|
"mopt": request.child_value("e/mopt"),
|
||||||
'lid': request.child_value('e/lid'),
|
"lid": request.child_value("e/lid"),
|
||||||
'sn': request.child_value('e/sn'),
|
"sn": request.child_value("e/sn"),
|
||||||
'pref': request.child_value('e/pref'),
|
"pref": request.child_value("e/pref"),
|
||||||
'stg': request.child_value('e/stg'),
|
"stg": request.child_value("e/stg"),
|
||||||
'pside': request.child_value('e/pside'),
|
"pside": request.child_value("e/pside"),
|
||||||
'eatime': request.child_value('e/eatime'),
|
"eatime": request.child_value("e/eatime"),
|
||||||
'ga': request.child_value('e/ga'),
|
"ga": request.child_value("e/ga"),
|
||||||
'gp': request.child_value('e/gp'),
|
"gp": request.child_value("e/gp"),
|
||||||
'la': request.child_value('e/la'),
|
"la": request.child_value("e/la"),
|
||||||
'ver': request.child_value('e/ver'),
|
"ver": request.child_value("e/ver"),
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
lobby = self.data.local.lobby.get_lobby(
|
lobby = self.data.local.lobby.get_lobby(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
)
|
)
|
||||||
root.add_child(Node.s32('eid', lobby.get_int('id')))
|
root.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e = Node.void('e')
|
e = Node.void("e")
|
||||||
root.add_child(e)
|
root.add_child(e)
|
||||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||||
e.add_child(Node.s32('uid', profile.extid))
|
e.add_child(Node.s32("uid", profile.extid))
|
||||||
e.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
e.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||||
e.add_child(Node.s32('plyid', info.get_int('id')))
|
e.add_child(Node.s32("plyid", info.get_int("id")))
|
||||||
e.add_child(Node.s16('mg', profile.get_int('mg')))
|
e.add_child(Node.s16("mg", profile.get_int("mg")))
|
||||||
e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
|
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("lid", lobby.get_str("lid")))
|
||||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
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("pref", lobby.get_int("pref")))
|
||||||
e.add_child(Node.s8('stg', lobby.get_int('stg')))
|
e.add_child(Node.s8("stg", lobby.get_int("stg")))
|
||||||
e.add_child(Node.s8('pside', lobby.get_int('pside')))
|
e.add_child(Node.s8("pside", lobby.get_int("pside")))
|
||||||
e.add_child(Node.s16('eatime', lobby.get_int('eatime')))
|
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.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
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_array("la", lobby.get_int_array("la", 4)))
|
||||||
e.add_child(Node.u8('ver', lobby.get_int('ver')))
|
e.add_child(Node.u8("ver", lobby.get_int("ver")))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_lobby_rb5_lobby_read_request(self, request: Node) -> Node:
|
def handle_lobby_rb5_lobby_read_request(self, request: Node) -> Node:
|
||||||
root = Node.void('lobby')
|
root = Node.void("lobby")
|
||||||
root.add_child(Node.s32('interval', 120))
|
root.add_child(Node.s32("interval", 120))
|
||||||
root.add_child(Node.s32('interval_p', 120))
|
root.add_child(Node.s32("interval_p", 120))
|
||||||
|
|
||||||
# Look up all lobbies matching the criteria specified
|
# Look up all lobbies matching the criteria specified
|
||||||
ver = request.child_value('var')
|
ver = request.child_value("var")
|
||||||
mg = request.child_value('m_grade') # noqa: F841
|
mg = request.child_value("m_grade") # noqa: F841
|
||||||
extid = request.child_value('uid')
|
extid = request.child_value("uid")
|
||||||
limit = request.child_value('max')
|
limit = request.child_value("max")
|
||||||
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
userid = self.data.remote.user.from_extid(self.game, self.version, extid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version)
|
lobbies = self.data.local.lobby.get_all_lobbies(self.game, self.version)
|
||||||
@ -331,95 +365,99 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
if user == userid:
|
if user == userid:
|
||||||
# If we have our own lobby, don't return it
|
# If we have our own lobby, don't return it
|
||||||
continue
|
continue
|
||||||
if ver != lobby.get_int('ver'):
|
if ver != lobby.get_int("ver"):
|
||||||
# Don't return lobby data for different versions
|
# Don't return lobby data for different versions
|
||||||
continue
|
continue
|
||||||
|
|
||||||
profile = self.get_profile(user)
|
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:
|
if profile is None or info is None:
|
||||||
# No profile info, don't return this lobby
|
# No profile info, don't return this lobby
|
||||||
return root
|
return root
|
||||||
|
|
||||||
e = Node.void('e')
|
e = Node.void("e")
|
||||||
root.add_child(e)
|
root.add_child(e)
|
||||||
e.add_child(Node.s32('eid', lobby.get_int('id')))
|
e.add_child(Node.s32("eid", lobby.get_int("id")))
|
||||||
e.add_child(Node.u16('mid', lobby.get_int('mid')))
|
e.add_child(Node.u16("mid", lobby.get_int("mid")))
|
||||||
e.add_child(Node.u8('ng', lobby.get_int('ng')))
|
e.add_child(Node.u8("ng", lobby.get_int("ng")))
|
||||||
e.add_child(Node.s32('uid', profile.extid))
|
e.add_child(Node.s32("uid", profile.extid))
|
||||||
e.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
e.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||||
e.add_child(Node.string('pn', profile.get_str('name')))
|
e.add_child(Node.string("pn", profile.get_str("name")))
|
||||||
e.add_child(Node.s32('plyid', info.get_int('id')))
|
e.add_child(Node.s32("plyid", info.get_int("id")))
|
||||||
e.add_child(Node.s16('mg', profile.get_int('mg')))
|
e.add_child(Node.s16("mg", profile.get_int("mg")))
|
||||||
e.add_child(Node.s32('mopt', lobby.get_int('mopt')))
|
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("lid", lobby.get_str("lid")))
|
||||||
e.add_child(Node.string('sn', lobby.get_str('sn')))
|
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("pref", lobby.get_int("pref")))
|
||||||
e.add_child(Node.s8('stg', lobby.get_int('stg')))
|
e.add_child(Node.s8("stg", lobby.get_int("stg")))
|
||||||
e.add_child(Node.s8('pside', lobby.get_int('pside')))
|
e.add_child(Node.s8("pside", lobby.get_int("pside")))
|
||||||
e.add_child(Node.s16('eatime', lobby.get_int('eatime')))
|
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.u8_array("ga", lobby.get_int_array("ga", 4)))
|
||||||
e.add_child(Node.u16('gp', lobby.get_int('gp')))
|
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_array("la", lobby.get_int_array("la", 4)))
|
||||||
e.add_child(Node.u8('ver', lobby.get_int('ver')))
|
e.add_child(Node.u8("ver", lobby.get_int("ver")))
|
||||||
|
|
||||||
limit = limit - 1
|
limit = limit - 1
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_lobby_rb5_lobby_delete_entry_request(self, request: Node) -> Node:
|
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)
|
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:
|
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)
|
machine = self.get_machine_by_id(shop_id)
|
||||||
if machine is not None:
|
if machine is not None:
|
||||||
machine_name = machine.name
|
machine_name = machine.name
|
||||||
close = machine.data.get_bool('close')
|
close = machine.data.get_bool("close")
|
||||||
hour = machine.data.get_int('hour')
|
hour = machine.data.get_int("hour")
|
||||||
minute = machine.data.get_int('minute')
|
minute = machine.data.get_int("minute")
|
||||||
else:
|
else:
|
||||||
machine_name = ''
|
machine_name = ""
|
||||||
close = False
|
close = False
|
||||||
hour = 0
|
hour = 0
|
||||||
minute = 0
|
minute = 0
|
||||||
|
|
||||||
root = Node.void('pcb')
|
root = Node.void("pcb")
|
||||||
sinfo = Node.void('sinfo')
|
sinfo = Node.void("sinfo")
|
||||||
root.add_child(sinfo)
|
root.add_child(sinfo)
|
||||||
sinfo.add_child(Node.string('nm', machine_name))
|
sinfo.add_child(Node.string("nm", machine_name))
|
||||||
sinfo.add_child(Node.bool('cl_enbl', close))
|
sinfo.add_child(Node.bool("cl_enbl", close))
|
||||||
sinfo.add_child(Node.u8('cl_h', hour))
|
sinfo.add_child(Node.u8("cl_h", hour))
|
||||||
sinfo.add_child(Node.u8('cl_m', minute))
|
sinfo.add_child(Node.u8("cl_m", minute))
|
||||||
sinfo.add_child(Node.bool('shop_flag', True))
|
sinfo.add_child(Node.bool("shop_flag", True))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_pcb_rb5_pcb_error_request(self, request: Node) -> Node:
|
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:
|
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:
|
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:
|
def handle_shop_rb5_shop_write_info_request(self, request: Node) -> Node:
|
||||||
self.update_machine_name(request.child_value('sinfo/nm'))
|
self.update_machine_name(request.child_value("sinfo/nm"))
|
||||||
self.update_machine_data({
|
self.update_machine_data(
|
||||||
'close': request.child_value('sinfo/cl_enbl'),
|
{
|
||||||
'hour': request.child_value('sinfo/cl_h'),
|
"close": request.child_value("sinfo/cl_enbl"),
|
||||||
'minute': request.child_value('sinfo/cl_m'),
|
"hour": request.child_value("sinfo/cl_h"),
|
||||||
'pref': request.child_value('sinfo/prf'),
|
"minute": request.child_value("sinfo/cl_m"),
|
||||||
})
|
"pref": request.child_value("sinfo/prf"),
|
||||||
return Node.void('shop')
|
}
|
||||||
|
)
|
||||||
|
return Node.void("shop")
|
||||||
|
|
||||||
def handle_player_rb5_player_start_request(self, request: Node) -> Node:
|
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
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
self.data.local.lobby.put_play_session_info(
|
self.data.local.lobby.put_play_session_info(
|
||||||
@ -427,10 +465,10 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
{
|
{
|
||||||
'ga': request.child_value('ga'),
|
"ga": request.child_value("ga"),
|
||||||
'gp': request.child_value('gp'),
|
"gp": request.child_value("gp"),
|
||||||
'la': request.child_value('la'),
|
"la": request.child_value("la"),
|
||||||
'pnid': request.child_value('pnid'),
|
"pnid": request.child_value("pnid"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
info = self.data.local.lobby.get_play_session_info(
|
info = self.data.local.lobby.get_play_session_info(
|
||||||
@ -439,22 +477,22 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
userid,
|
userid,
|
||||||
)
|
)
|
||||||
if info is not None:
|
if info is not None:
|
||||||
play_id = info.get_int('id')
|
play_id = info.get_int("id")
|
||||||
else:
|
else:
|
||||||
play_id = 0
|
play_id = 0
|
||||||
else:
|
else:
|
||||||
play_id = 0
|
play_id = 0
|
||||||
|
|
||||||
# Session stuff, and resend global defaults
|
# Session stuff, and resend global defaults
|
||||||
root.add_child(Node.s32('plyid', play_id))
|
root.add_child(Node.s32("plyid", play_id))
|
||||||
root.add_child(Node.u64('start_time', Time.now() * 1000))
|
root.add_child(Node.u64("start_time", Time.now() * 1000))
|
||||||
self._add_event_info(root)
|
self._add_event_info(root)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_player_rb5_player_end_request(self, request: Node) -> Node:
|
def handle_player_rb5_player_end_request(self, request: Node) -> Node:
|
||||||
# Destroy play session based on info from the request
|
# 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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
# Kill any lingering lobbies by this user
|
# Kill any lingering lobbies by this user
|
||||||
@ -464,16 +502,18 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
userid,
|
userid,
|
||||||
)
|
)
|
||||||
if lobby is not None:
|
if lobby is not None:
|
||||||
self.data.local.lobby.destroy_lobby(lobby.get_int('id'))
|
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_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:
|
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:
|
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)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
previous_version = self.previous_version()
|
previous_version = self.previous_version()
|
||||||
@ -481,25 +521,25 @@ class ReflecBeatVolzzaBase(ReflecBeatBase):
|
|||||||
else:
|
else:
|
||||||
profile = None
|
profile = None
|
||||||
|
|
||||||
root = Node.void('player')
|
root = Node.void("player")
|
||||||
|
|
||||||
if profile is None:
|
if profile is None:
|
||||||
# Return empty succeed to say this is new
|
# Return empty succeed to say this is new
|
||||||
root.add_child(Node.string('name', ''))
|
root.add_child(Node.string("name", ""))
|
||||||
root.add_child(Node.s32('grd', -1))
|
root.add_child(Node.s32("grd", -1))
|
||||||
root.add_child(Node.s32('ap', -1))
|
root.add_child(Node.s32("ap", -1))
|
||||||
root.add_child(Node.s32('uattr', 0))
|
root.add_child(Node.s32("uattr", 0))
|
||||||
else:
|
else:
|
||||||
# Return previous profile formatted to say this is data succession
|
# Return previous profile formatted to say this is data succession
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
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("grd", profile.get_int("mg"))) # This is a guess
|
||||||
root.add_child(Node.s32('ap', profile.get_int('ap')))
|
root.add_child(Node.s32("ap", profile.get_int("ap")))
|
||||||
root.add_child(Node.s32('uattr', profile.get_int('uattr')))
|
root.add_child(Node.s32("uattr", profile.get_int("uattr")))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_player_rb5_player_read_request(self, request: Node) -> Node:
|
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)
|
profile = self.get_profile_by_refid(refid)
|
||||||
if profile:
|
if profile:
|
||||||
return 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_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_CLEAR
|
||||||
CLEAR_TYPE_HARD_CLEAR: Final[int] = DBConstants.SDVX_CLEAR_TYPE_HARD_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_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_NO_PLAY: Final[int] = DBConstants.SDVX_GRADE_NO_PLAY
|
||||||
GRADE_D: Final[int] = DBConstants.SDVX_GRADE_D
|
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_INFINITE: Final[int] = 3
|
||||||
CHART_TYPE_MAXIMUM: Final[int] = 4
|
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
|
Returns the previous version of the game, based on this game. Should
|
||||||
be overridden.
|
be overridden.
|
||||||
@ -66,7 +68,9 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
# Now, return it
|
# Now, return it
|
||||||
return self.format_profile(userid, profile)
|
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
|
Given a RefID and an optional name, create a profile and then return
|
||||||
a formatted profile node. Similar rationale to get_profile_by_refid.
|
a formatted profile node. Similar rationale to get_profile_by_refid.
|
||||||
@ -75,7 +79,7 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if name is None:
|
if name is None:
|
||||||
name = 'NONAME'
|
name = "NONAME"
|
||||||
|
|
||||||
# First, create and save the default profile
|
# First, create and save the default profile
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -85,8 +89,8 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
refid,
|
refid,
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
'name': name,
|
"name": name,
|
||||||
'loc': locid,
|
"loc": locid,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
self.put_profile(userid, profile)
|
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,
|
Base handler for a profile. Given a userid and a profile dictionary,
|
||||||
return a Node representing a profile. Should be overridden.
|
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,
|
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.
|
return a new profile that's been updated with the contents of the request.
|
||||||
@ -121,7 +127,8 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
all_attempts, remote_attempts = Parallel.execute([
|
all_attempts, remote_attempts = Parallel.execute(
|
||||||
|
[
|
||||||
lambda: self.data.local.music.get_all_attempts(
|
lambda: self.data.local.music.get_all_attempts(
|
||||||
game=self.game,
|
game=self.game,
|
||||||
version=self.version,
|
version=self.version,
|
||||||
@ -129,8 +136,9 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
lambda: self.data.remote.music.get_clear_rates(
|
lambda: self.data.remote.music.get_clear_rates(
|
||||||
game=self.game,
|
game=self.game,
|
||||||
version=self.version,
|
version=self.version,
|
||||||
|
),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
])
|
|
||||||
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
attempts: Dict[int, Dict[int, Dict[str, int]]] = {}
|
||||||
for (_, attempt) in all_attempts:
|
for (_, attempt) in all_attempts:
|
||||||
# Terrible temporary structure is terrible.
|
# Terrible temporary structure is terrible.
|
||||||
@ -138,26 +146,33 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
attempts[attempt.id] = {}
|
attempts[attempt.id] = {}
|
||||||
if attempt.chart not in attempts[attempt.id]:
|
if attempt.chart not in attempts[attempt.id]:
|
||||||
attempts[attempt.id][attempt.chart] = {
|
attempts[attempt.id][attempt.chart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'average': 0,
|
"average": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
# We saw an attempt, keep the total attempts in sync.
|
# 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]["average"]
|
||||||
) / (attempts[attempt.id][attempt.chart]['total'] + 1)
|
* attempts[attempt.id][attempt.chart]["total"]
|
||||||
)
|
)
|
||||||
attempts[attempt.id][attempt.chart]['total'] += 1
|
+ attempt.points
|
||||||
|
)
|
||||||
|
/ (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
|
# This attempt was a failure, so don't count it against clears of full combos
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# It was at least a clear
|
# It was at least a clear
|
||||||
attempts[attempt.id][attempt.chart]['clears'] += 1
|
attempts[attempt.id][attempt.chart]["clears"] += 1
|
||||||
|
|
||||||
# Merge in remote attempts
|
# Merge in remote attempts
|
||||||
for songid in remote_attempts:
|
for songid in remote_attempts:
|
||||||
@ -167,13 +182,17 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
for songchart in remote_attempts[songid]:
|
for songchart in remote_attempts[songid]:
|
||||||
if songchart not in attempts[songid]:
|
if songchart not in attempts[songid]:
|
||||||
attempts[songid][songchart] = {
|
attempts[songid][songchart] = {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'clears': 0,
|
"clears": 0,
|
||||||
'average': 0,
|
"average": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts[songid][songchart]['total'] += remote_attempts[songid][songchart]['plays']
|
attempts[songid][songchart]["total"] += remote_attempts[songid][
|
||||||
attempts[songid][songchart]['clears'] += remote_attempts[songid][songchart]['clears']
|
songchart
|
||||||
|
]["plays"]
|
||||||
|
attempts[songid][songchart]["clears"] += remote_attempts[songid][
|
||||||
|
songchart
|
||||||
|
]["clears"]
|
||||||
|
|
||||||
return attempts
|
return attempts
|
||||||
|
|
||||||
@ -248,21 +267,23 @@ class SoundVoltexBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
|||||||
scoredata = oldscore.data
|
scoredata = oldscore.data
|
||||||
|
|
||||||
# Replace clear type and grade
|
# Replace clear type and grade
|
||||||
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
|
scoredata.replace_int(
|
||||||
history.replace_int('clear_type', clear_type)
|
"clear_type", max(scoredata.get_int("clear_type"), clear_type)
|
||||||
scoredata.replace_int('grade', max(scoredata.get_int('grade'), grade))
|
)
|
||||||
history.replace_int('grade', grade)
|
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
|
# If we have a combo, replace it
|
||||||
scoredata.replace_int('combo', max(scoredata.get_int('combo'), combo))
|
scoredata.replace_int("combo", max(scoredata.get_int("combo"), combo))
|
||||||
history.replace_int('combo', combo)
|
history.replace_int("combo", combo)
|
||||||
|
|
||||||
# If we have play stats, replace it
|
# If we have play stats, replace it
|
||||||
if stats is not None:
|
if stats is not None:
|
||||||
if raised:
|
if raised:
|
||||||
# We have stats, and there's a new high score, update the stats
|
# We have stats, and there's a new high score, update the stats
|
||||||
scoredata.replace_dict('stats', stats)
|
scoredata.replace_dict("stats", stats)
|
||||||
history.replace_dict('stats', stats)
|
history.replace_dict("stats", stats)
|
||||||
|
|
||||||
# Look up where this score was earned
|
# Look up where this score was earned
|
||||||
lid = self.get_machine_id()
|
lid = self.get_machine_id()
|
||||||
|
@ -14,7 +14,7 @@ class SoundVoltexBooth(
|
|||||||
SoundVoltexBase,
|
SoundVoltexBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'SOUND VOLTEX BOOTH'
|
name: str = "SOUND VOLTEX BOOTH"
|
||||||
version: int = VersionConstants.SDVX_BOOTH
|
version: int = VersionConstants.SDVX_BOOTH
|
||||||
|
|
||||||
GAME_LIMITED_LOCKED: Final[int] = 1
|
GAME_LIMITED_LOCKED: Final[int] = 1
|
||||||
@ -42,24 +42,24 @@ class SoundVoltexBooth(
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Disable Online Matching',
|
"name": "Disable Online Matching",
|
||||||
'tip': 'Disable online matching between games.',
|
"tip": "Disable online matching between games.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'disable_matching',
|
"setting": "disable_matching",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Appeal Card Unlock',
|
"name": "Force Appeal Card Unlock",
|
||||||
'tip': 'Force unlock all appeal cards.',
|
"tip": "Force unlock all appeal cards.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_cards',
|
"setting": "force_unlock_cards",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -112,108 +112,108 @@ class SoundVoltexBooth(
|
|||||||
}[grade]
|
}[grade]
|
||||||
|
|
||||||
def handle_game_exception_request(self, request: Node) -> Node:
|
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:
|
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.
|
# 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
|
return game
|
||||||
|
|
||||||
def handle_game_lounge_request(self, request: Node) -> Node:
|
def handle_game_lounge_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
# Refresh interval in seconds.
|
# Refresh interval in seconds.
|
||||||
game.add_child(Node.u32('interval', 10))
|
game.add_child(Node.u32("interval", 10))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_entry_e_request(self, request: Node) -> Node:
|
def handle_game_entry_e_request(self, request: Node) -> Node:
|
||||||
# Lobby destroy method, eid attribute (u32) should be used
|
# Lobby destroy method, eid attribute (u32) should be used
|
||||||
# to destroy any open lobbies.
|
# to destroy any open lobbies.
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
def handle_game_frozen_request(self, request: Node) -> Node:
|
def handle_game_frozen_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.set_attribute('result', '0')
|
game.set_attribute("result", "0")
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_shop_request(self, request: Node) -> Node:
|
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
|
# Respond with number of milliseconds until next request
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_common_request(self, request: Node) -> Node:
|
def handle_game_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
limited = Node.void('limited')
|
limited = Node.void("limited")
|
||||||
game.add_child(limited)
|
game.add_child(limited)
|
||||||
|
|
||||||
game_config = self.get_game_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()
|
ids = set()
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
||||||
for song in songs:
|
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)
|
ids.add(song.id)
|
||||||
|
|
||||||
for songid in ids:
|
for songid in ids:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
limited.add_child(music)
|
limited.add_child(music)
|
||||||
music.set_attribute('id', str(songid))
|
music.set_attribute("id", str(songid))
|
||||||
music.set_attribute('flag', str(self.GAME_LIMITED_UNLOCKED))
|
music.set_attribute("flag", str(self.GAME_LIMITED_UNLOCKED))
|
||||||
|
|
||||||
event = Node.void('event')
|
event = Node.void("event")
|
||||||
game.add_child(event)
|
game.add_child(event)
|
||||||
|
|
||||||
def enable_event(eid: int) -> None:
|
def enable_event(eid: int) -> None:
|
||||||
evt = Node.void('info')
|
evt = Node.void("info")
|
||||||
event.add_child(evt)
|
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(3) # Matching enabled
|
||||||
enable_event(9) # Rank Soukuu
|
enable_event(9) # Rank Soukuu
|
||||||
enable_event(13) # Year-end bonus
|
enable_event(13) # Year-end bonus
|
||||||
|
|
||||||
catalog = Node.void('catalog')
|
catalog = Node.void("catalog")
|
||||||
game.add_child(catalog)
|
game.add_child(catalog)
|
||||||
songunlocks = self.data.local.game.get_items(self.game, self.version)
|
songunlocks = self.data.local.game.get_items(self.game, self.version)
|
||||||
for unlock in songunlocks:
|
for unlock in songunlocks:
|
||||||
if unlock.type != 'song_unlock':
|
if unlock.type != "song_unlock":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
catalog.add_child(info)
|
catalog.add_child(info)
|
||||||
info.set_attribute('id', str(unlock.id))
|
info.set_attribute("id", str(unlock.id))
|
||||||
info.set_attribute('currency', str(self.GAME_CURRENCY_BLOCKS))
|
info.set_attribute("currency", str(self.GAME_CURRENCY_BLOCKS))
|
||||||
info.set_attribute('price', str(unlock.data.get_int('blocks')))
|
info.set_attribute("price", str(unlock.data.get_int("blocks")))
|
||||||
|
|
||||||
kacinfo = Node.void('kacinfo')
|
kacinfo = Node.void("kacinfo")
|
||||||
game.add_child(kacinfo)
|
game.add_child(kacinfo)
|
||||||
kacinfo.add_child(Node.u32('note00', 0))
|
kacinfo.add_child(Node.u32("note00", 0))
|
||||||
kacinfo.add_child(Node.u32('note01', 0))
|
kacinfo.add_child(Node.u32("note01", 0))
|
||||||
kacinfo.add_child(Node.u32('note02', 0))
|
kacinfo.add_child(Node.u32("note02", 0))
|
||||||
kacinfo.add_child(Node.u32('note10', 0))
|
kacinfo.add_child(Node.u32("note10", 0))
|
||||||
kacinfo.add_child(Node.u32('note11', 0))
|
kacinfo.add_child(Node.u32("note11", 0))
|
||||||
kacinfo.add_child(Node.u32('note12', 0))
|
kacinfo.add_child(Node.u32("note12", 0))
|
||||||
kacinfo.add_child(Node.u32('rabbeat0', 0))
|
kacinfo.add_child(Node.u32("rabbeat0", 0))
|
||||||
kacinfo.add_child(Node.u32('rabbeat1', 0))
|
kacinfo.add_child(Node.u32("rabbeat1", 0))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_hiscore_request(self, request: Node) -> Node:
|
def handle_game_hiscore_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
|
|
||||||
# Ranking system I think?
|
# Ranking system I think?
|
||||||
for i in range(1, 21):
|
for i in range(1, 21):
|
||||||
ranking = Node.void('ranking')
|
ranking = Node.void("ranking")
|
||||||
game.add_child(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)
|
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)
|
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
|
records_by_id[score.id][score.chart] = record
|
||||||
missing_users.append(userid)
|
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
|
# Output records
|
||||||
for songid in records_by_id:
|
for songid in records_by_id:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
hiscore.add_child(music)
|
hiscore.add_child(music)
|
||||||
music.set_attribute('id', str(songid))
|
music.set_attribute("id", str(songid))
|
||||||
|
|
||||||
for chart in records_by_id[songid]:
|
for chart in records_by_id[songid]:
|
||||||
note = Node.void('note')
|
note = Node.void("note")
|
||||||
music.add_child(note)
|
music.add_child(note)
|
||||||
note.set_attribute('type', str(chart))
|
note.set_attribute("type", str(chart))
|
||||||
|
|
||||||
userid, score = records_by_id[songid][chart]
|
userid, score = records_by_id[songid][chart]
|
||||||
note.set_attribute('score', str(score.points))
|
note.set_attribute("score", str(score.points))
|
||||||
note.set_attribute('name', users[userid].get_str('name'))
|
note.set_attribute("name", users[userid].get_str("name"))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_new_request(self, request: Node) -> Node:
|
def handle_game_new_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
name = request.attribute('name')
|
name = request.attribute("name")
|
||||||
loc = ID.parse_machine_id(request.attribute('locid'))
|
loc = ID.parse_machine_id(request.attribute("locid"))
|
||||||
self.new_profile_by_refid(refid, name, loc)
|
self.new_profile_by_refid(refid, name, loc)
|
||||||
|
|
||||||
root = Node.void('game')
|
root = Node.void("game")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_load_request(self, request: Node) -> Node:
|
def handle_game_load_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('dataid')
|
refid = request.attribute("dataid")
|
||||||
root = self.get_profile_by_refid(refid)
|
root = self.get_profile_by_refid(refid)
|
||||||
if root is None:
|
if root is None:
|
||||||
root = Node.void('game')
|
root = Node.void("game")
|
||||||
root.set_attribute('none', '1')
|
root.set_attribute("none", "1")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_save_request(self, request: Node) -> Node:
|
def handle_game_save_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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:
|
if userid is not None and newprofile is not None:
|
||||||
self.put_profile(userid, newprofile)
|
self.put_profile(userid, newprofile)
|
||||||
|
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
def handle_game_load_m_request(self, request: Node) -> Node:
|
def handle_game_load_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('dataid')
|
refid = request.attribute("dataid")
|
||||||
|
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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
|
scores_by_id[score.id][score.chart] = score
|
||||||
|
|
||||||
# Output to the game
|
# Output to the game
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
for songid in scores_by_id:
|
for songid in scores_by_id:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
game.add_child(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]:
|
for chart in scores_by_id[songid]:
|
||||||
typenode = Node.void('type')
|
typenode = Node.void("type")
|
||||||
music.add_child(typenode)
|
music.add_child(typenode)
|
||||||
typenode.set_attribute('type_id', str(chart))
|
typenode.set_attribute("type_id", str(chart))
|
||||||
|
|
||||||
score = scores_by_id[songid][chart]
|
score = scores_by_id[songid][chart]
|
||||||
typenode.set_attribute('score', str(score.points))
|
typenode.set_attribute("score", str(score.points))
|
||||||
typenode.set_attribute('cnt', str(score.plays))
|
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(
|
||||||
typenode.set_attribute('score_grade', str(self.__db_to_game_grade(score.data.get_int('grade'))))
|
"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
|
return game
|
||||||
|
|
||||||
def handle_game_save_m_request(self, request: Node) -> Node:
|
def handle_game_save_m_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('dataid')
|
refid = request.attribute("dataid")
|
||||||
|
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -333,14 +342,14 @@ class SoundVoltexBooth(
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
if userid is None:
|
if userid is None:
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
musicid = int(request.attribute('music_id'))
|
musicid = int(request.attribute("music_id"))
|
||||||
chart = int(request.attribute('music_type'))
|
chart = int(request.attribute("music_type"))
|
||||||
score = int(request.attribute('score'))
|
score = int(request.attribute("score"))
|
||||||
combo = int(request.attribute('max_chain'))
|
combo = int(request.attribute("max_chain"))
|
||||||
grade = self.__game_to_db_grade(int(request.attribute('score_grade')))
|
grade = self.__game_to_db_grade(int(request.attribute("score_grade")))
|
||||||
clear_type = self.__game_to_db_clear_type(int(request.attribute('clear_type')))
|
clear_type = self.__game_to_db_clear_type(int(request.attribute("clear_type")))
|
||||||
|
|
||||||
# Save the score
|
# Save the score
|
||||||
self.update_score(
|
self.update_score(
|
||||||
@ -354,10 +363,10 @@ class SoundVoltexBooth(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# No response necessary
|
# No response necessary
|
||||||
return Node.void('game')
|
return Node.void("game")
|
||||||
|
|
||||||
def handle_game_buy_request(self, request: Node) -> Node:
|
def handle_game_buy_request(self, request: Node) -> Node:
|
||||||
refid = request.attribute('refid')
|
refid = request.attribute("refid")
|
||||||
|
|
||||||
if refid is not None:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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:
|
if userid is not None and profile is not None:
|
||||||
# Look up packets and blocks
|
# Look up packets and blocks
|
||||||
packet = profile.get_int('packet')
|
packet = profile.get_int("packet")
|
||||||
block = profile.get_int('block')
|
block = profile.get_int("block")
|
||||||
|
|
||||||
# Add on any additional we earned this round
|
# Add on any additional we earned this round
|
||||||
packet = packet + (request.child_value('earned_gamecoin_packet') or 0)
|
packet = packet + (request.child_value("earned_gamecoin_packet") or 0)
|
||||||
block = block + (request.child_value('earned_gamecoin_block') or 0)
|
block = block + (request.child_value("earned_gamecoin_block") or 0)
|
||||||
|
|
||||||
# Look up the item to get the actual price and currency used
|
# 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:
|
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 currency_type == self.GAME_CURRENCY_PACKETS:
|
||||||
if 'packets' in item:
|
if "packets" in item:
|
||||||
# This is a valid purchase
|
# This is a valid purchase
|
||||||
newpacket = packet - item.get_int('packets')
|
newpacket = packet - item.get_int("packets")
|
||||||
if newpacket < 0:
|
if newpacket < 0:
|
||||||
result = 1
|
result = 1
|
||||||
else:
|
else:
|
||||||
@ -395,9 +409,9 @@ class SoundVoltexBooth(
|
|||||||
# Bad transaction
|
# Bad transaction
|
||||||
result = 1
|
result = 1
|
||||||
elif currency_type == self.GAME_CURRENCY_BLOCKS:
|
elif currency_type == self.GAME_CURRENCY_BLOCKS:
|
||||||
if 'blocks' in item:
|
if "blocks" in item:
|
||||||
# This is a valid purchase
|
# This is a valid purchase
|
||||||
newblock = block - item.get_int('blocks')
|
newblock = block - item.get_int("blocks")
|
||||||
if newblock < 0:
|
if newblock < 0:
|
||||||
result = 1
|
result = 1
|
||||||
else:
|
else:
|
||||||
@ -412,8 +426,8 @@ class SoundVoltexBooth(
|
|||||||
|
|
||||||
if result == 0:
|
if result == 0:
|
||||||
# Transaction is valid, update the profile with new packets and blocks
|
# Transaction is valid, update the profile with new packets and blocks
|
||||||
profile.replace_int('packet', packet)
|
profile.replace_int("packet", packet)
|
||||||
profile.replace_int('block', block)
|
profile.replace_int("block", block)
|
||||||
self.put_profile(userid, profile)
|
self.put_profile(userid, profile)
|
||||||
else:
|
else:
|
||||||
# Bad catalog ID
|
# Bad catalog ID
|
||||||
@ -424,97 +438,117 @@ class SoundVoltexBooth(
|
|||||||
block = 0
|
block = 0
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
game.add_child(Node.u32('gamecoin_packet', packet))
|
game.add_child(Node.u32("gamecoin_packet", packet))
|
||||||
game.add_child(Node.u32('gamecoin_block', block))
|
game.add_child(Node.u32("gamecoin_block", block))
|
||||||
game.add_child(Node.s8('result', result))
|
game.add_child(Node.s8("result", result))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
def format_profile(self, userid: UserID, profile: Profile) -> Node:
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
|
|
||||||
# Generic profile stuff
|
# Generic profile stuff
|
||||||
game.add_child(Node.string('name', profile.get_str('name')))
|
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.string("code", ID.format_extid(profile.extid)))
|
||||||
game.add_child(Node.u32('gamecoin_packet', profile.get_int('packet')))
|
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("gamecoin_block", profile.get_int("block")))
|
||||||
game.add_child(Node.u32('exp_point', profile.get_int('exp')))
|
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.u32("m_user_cnt", profile.get_int("m_user_cnt")))
|
||||||
|
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
if game_config.get_bool('force_unlock_cards'):
|
if game_config.get_bool("force_unlock_cards"):
|
||||||
game.add_child(Node.bool_array('have_item', [True] * 512))
|
game.add_child(Node.bool_array("have_item", [True] * 512))
|
||||||
else:
|
else:
|
||||||
game.add_child(Node.bool_array('have_item', [x > 0 for x in profile.get_int_array('have_item', 512)]))
|
game.add_child(
|
||||||
if game_config.get_bool('force_unlock_songs'):
|
Node.bool_array(
|
||||||
game.add_child(Node.bool_array('have_note', [True] * 512))
|
"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:
|
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
|
# Last played stuff
|
||||||
lastdict = profile.get_dict('last')
|
lastdict = profile.get_dict("last")
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
game.add_child(last)
|
game.add_child(last)
|
||||||
last.set_attribute('music_id', str(lastdict.get_int('music_id')))
|
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("music_type", str(lastdict.get_int("music_type")))
|
||||||
last.set_attribute('sort_type', str(lastdict.get_int('sort_type')))
|
last.set_attribute("sort_type", str(lastdict.get_int("sort_type")))
|
||||||
last.set_attribute('headphone', str(lastdict.get_int('headphone')))
|
last.set_attribute("headphone", str(lastdict.get_int("headphone")))
|
||||||
last.set_attribute('hispeed', str(lastdict.get_int('hispeed')))
|
last.set_attribute("hispeed", str(lastdict.get_int("hispeed")))
|
||||||
last.set_attribute('appeal_id', str(lastdict.get_int('appeal_id')))
|
last.set_attribute("appeal_id", str(lastdict.get_int("appeal_id")))
|
||||||
last.set_attribute('frame0', str(lastdict.get_int('frame0')))
|
last.set_attribute("frame0", str(lastdict.get_int("frame0")))
|
||||||
last.set_attribute('frame1', str(lastdict.get_int('frame1')))
|
last.set_attribute("frame1", str(lastdict.get_int("frame1")))
|
||||||
last.set_attribute('frame2', str(lastdict.get_int('frame2')))
|
last.set_attribute("frame2", str(lastdict.get_int("frame2")))
|
||||||
last.set_attribute('frame3', str(lastdict.get_int('frame3')))
|
last.set_attribute("frame3", str(lastdict.get_int("frame3")))
|
||||||
last.set_attribute('frame4', str(lastdict.get_int('frame4')))
|
last.set_attribute("frame4", str(lastdict.get_int("frame4")))
|
||||||
|
|
||||||
return game
|
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()
|
newprofile = oldprofile.clone()
|
||||||
|
|
||||||
# Update experience and in-game currencies
|
# 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:
|
if earned_gamecoin_packet is not None:
|
||||||
newprofile.replace_int('packet', newprofile.get_int('packet') + earned_gamecoin_packet)
|
newprofile.replace_int(
|
||||||
earned_gamecoin_block = request.child_value('earned_gamecoin_block')
|
"packet", newprofile.get_int("packet") + earned_gamecoin_packet
|
||||||
|
)
|
||||||
|
earned_gamecoin_block = request.child_value("earned_gamecoin_block")
|
||||||
if earned_gamecoin_block is not None:
|
if earned_gamecoin_block is not None:
|
||||||
newprofile.replace_int('block', newprofile.get_int('block') + earned_gamecoin_block)
|
newprofile.replace_int(
|
||||||
gain_exp = request.child_value('gain_exp')
|
"block", newprofile.get_int("block") + earned_gamecoin_block
|
||||||
|
)
|
||||||
|
gain_exp = request.child_value("gain_exp")
|
||||||
if gain_exp is not None:
|
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
|
# 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
|
# Update user's unlock status if we aren't force unlocked
|
||||||
game_config = self.get_game_config()
|
game_config = self.get_game_config()
|
||||||
if not game_config.get_bool('force_unlock_cards'):
|
if not game_config.get_bool("force_unlock_cards"):
|
||||||
have_item = request.child_value('have_item')
|
have_item = request.child_value("have_item")
|
||||||
if have_item is not None:
|
if have_item is not None:
|
||||||
newprofile.replace_int_array('have_item', 512, [1 if x else 0 for x in have_item])
|
newprofile.replace_int_array(
|
||||||
if not game_config.get_bool('force_unlock_songs'):
|
"have_item", 512, [1 if x else 0 for x in have_item]
|
||||||
have_note = request.child_value('have_note')
|
)
|
||||||
|
if not game_config.get_bool("force_unlock_songs"):
|
||||||
|
have_note = request.child_value("have_note")
|
||||||
if have_note is not None:
|
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.
|
# Grab last information.
|
||||||
lastdict = newprofile.get_dict('last')
|
lastdict = newprofile.get_dict("last")
|
||||||
lastdict.replace_int('headphone', request.child_value('headphone'))
|
lastdict.replace_int("headphone", request.child_value("headphone"))
|
||||||
lastdict.replace_int('hispeed', request.child_value('hispeed'))
|
lastdict.replace_int("hispeed", request.child_value("hispeed"))
|
||||||
lastdict.replace_int('appeal_id', request.child_value('appeal_id'))
|
lastdict.replace_int("appeal_id", request.child_value("appeal_id"))
|
||||||
lastdict.replace_int('frame0', request.child_value('frame0'))
|
lastdict.replace_int("frame0", request.child_value("frame0"))
|
||||||
lastdict.replace_int('frame1', request.child_value('frame1'))
|
lastdict.replace_int("frame1", request.child_value("frame1"))
|
||||||
lastdict.replace_int('frame2', request.child_value('frame2'))
|
lastdict.replace_int("frame2", request.child_value("frame2"))
|
||||||
lastdict.replace_int('frame3', request.child_value('frame3'))
|
lastdict.replace_int("frame3", request.child_value("frame3"))
|
||||||
lastdict.replace_int('frame4', request.child_value('frame4'))
|
lastdict.replace_int("frame4", request.child_value("frame4"))
|
||||||
last = request.child('last')
|
last = request.child("last")
|
||||||
if last is not None:
|
if last is not None:
|
||||||
lastdict.replace_int('music_id', intish(last.attribute('music_id')))
|
lastdict.replace_int("music_id", intish(last.attribute("music_id")))
|
||||||
lastdict.replace_int('music_type', intish(last.attribute('music_type')))
|
lastdict.replace_int("music_type", intish(last.attribute("music_type")))
|
||||||
lastdict.replace_int('sort_type', intish(last.attribute('sort_type')))
|
lastdict.replace_int("sort_type", intish(last.attribute("sort_type")))
|
||||||
|
|
||||||
# Save back last information gleaned from results
|
# Save back last information gleaned from results
|
||||||
newprofile.replace_dict('last', lastdict)
|
newprofile.replace_dict("last", lastdict)
|
||||||
|
|
||||||
# Keep track of play statistics
|
# Keep track of play statistics
|
||||||
self.update_play_statistics(userid)
|
self.update_play_statistics(userid)
|
||||||
|
@ -22,12 +22,17 @@ class SoundVoltexFactory(Factory):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_all(cls) -> None:
|
def register_all(cls) -> None:
|
||||||
for gamecode in ['KFC']:
|
for gamecode in ["KFC"]:
|
||||||
Base.register(gamecode, SoundVoltexFactory)
|
Base.register(gamecode, SoundVoltexFactory)
|
||||||
|
|
||||||
@classmethod
|
@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]:
|
def version_from_date(date: int) -> Optional[int]:
|
||||||
if date < 2013060500:
|
if date < 2013060500:
|
||||||
return VersionConstants.SDVX_BOOTH
|
return VersionConstants.SDVX_BOOTH
|
||||||
@ -39,14 +44,14 @@ class SoundVoltexFactory(Factory):
|
|||||||
return VersionConstants.SDVX_HEAVENLY_HAVEN
|
return VersionConstants.SDVX_HEAVENLY_HAVEN
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if model.gamecode == 'KFC':
|
if model.gamecode == "KFC":
|
||||||
if model.version is None:
|
if model.version is None:
|
||||||
if parentmodel is None:
|
if parentmodel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# We have no way to tell apart newer versions. However, we can make
|
# 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.
|
# an educated guess if we happen to be summoned for old profile lookup.
|
||||||
if parentmodel.gamecode != 'KFC':
|
if parentmodel.gamecode != "KFC":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
parentversion = version_from_date(parentmodel.version)
|
parentversion = version_from_date(parentmodel.version)
|
||||||
|
@ -14,7 +14,7 @@ class SoundVoltexGravityWars(
|
|||||||
SoundVoltexBase,
|
SoundVoltexBase,
|
||||||
):
|
):
|
||||||
|
|
||||||
name: str = 'SOUND VOLTEX III GRAVITY WARS'
|
name: str = "SOUND VOLTEX III GRAVITY WARS"
|
||||||
version: int = VersionConstants.SDVX_GRAVITY_WARS
|
version: int = VersionConstants.SDVX_GRAVITY_WARS
|
||||||
|
|
||||||
GAME_LIMITED_LOCKED: Final[int] = 1
|
GAME_LIMITED_LOCKED: Final[int] = 1
|
||||||
@ -50,30 +50,30 @@ class SoundVoltexGravityWars(
|
|||||||
Return all of our front-end modifiably settings.
|
Return all of our front-end modifiably settings.
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'bools': [
|
"bools": [
|
||||||
{
|
{
|
||||||
'name': 'Disable Online Matching',
|
"name": "Disable Online Matching",
|
||||||
'tip': 'Disable online matching between games.',
|
"tip": "Disable online matching between games.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'disable_matching',
|
"setting": "disable_matching",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Song Unlock',
|
"name": "Force Song Unlock",
|
||||||
'tip': 'Force unlock all songs.',
|
"tip": "Force unlock all songs.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_songs',
|
"setting": "force_unlock_songs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Appeal Card Unlock',
|
"name": "Force Appeal Card Unlock",
|
||||||
'tip': 'Force unlock all appeal cards.',
|
"tip": "Force unlock all appeal cards.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_cards',
|
"setting": "force_unlock_cards",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Force Crew Card Unlock',
|
"name": "Force Crew Card Unlock",
|
||||||
'tip': 'Force unlock all crew and subcrew cards.',
|
"tip": "Force unlock all crew and subcrew cards.",
|
||||||
'category': 'game_config',
|
"category": "game_config",
|
||||||
'setting': 'force_unlock_crew',
|
"setting": "force_unlock_crew",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -140,51 +140,54 @@ class SoundVoltexGravityWars(
|
|||||||
|
|
||||||
def __get_skill_analyzer_skill_levels(self) -> Dict[int, str]:
|
def __get_skill_analyzer_skill_levels(self) -> Dict[int, str]:
|
||||||
return {
|
return {
|
||||||
0: 'Skill LEVEL 01 岳翔',
|
0: "Skill LEVEL 01 岳翔",
|
||||||
1: 'Skill LEVEL 02 流星',
|
1: "Skill LEVEL 02 流星",
|
||||||
2: 'Skill LEVEL 03 月衝',
|
2: "Skill LEVEL 03 月衝",
|
||||||
3: 'Skill LEVEL 04 瞬光',
|
3: "Skill LEVEL 04 瞬光",
|
||||||
4: 'Skill LEVEL 05 天極',
|
4: "Skill LEVEL 05 天極",
|
||||||
5: 'Skill LEVEL 06 烈風',
|
5: "Skill LEVEL 06 烈風",
|
||||||
6: 'Skill LEVEL 07 雷電',
|
6: "Skill LEVEL 07 雷電",
|
||||||
7: 'Skill LEVEL 08 麗華',
|
7: "Skill LEVEL 08 麗華",
|
||||||
8: 'Skill LEVEL 09 魔騎士',
|
8: "Skill LEVEL 09 魔騎士",
|
||||||
9: 'Skill LEVEL 10 剛力羅',
|
9: "Skill LEVEL 10 剛力羅",
|
||||||
10: 'Skill LEVEL 11 或帝滅斗',
|
10: "Skill LEVEL 11 或帝滅斗",
|
||||||
11: 'Skill LEVEL ∞(12) 暴龍天',
|
11: "Skill LEVEL ∞(12) 暴龍天",
|
||||||
}
|
}
|
||||||
|
|
||||||
def handle_game_3_common_request(self, request: Node) -> Node:
|
def handle_game_3_common_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
limited = Node.void('music_limited')
|
limited = Node.void("music_limited")
|
||||||
game.add_child(limited)
|
game.add_child(limited)
|
||||||
|
|
||||||
# Song unlock config
|
# Song unlock config
|
||||||
game_config = self.get_game_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()
|
ids = set()
|
||||||
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
songs = self.data.local.music.get_all_songs(self.game, self.version)
|
||||||
for song in songs:
|
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))
|
ids.add((song.id, song.chart))
|
||||||
|
|
||||||
for (songid, chart) in ids:
|
for (songid, chart) in ids:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
limited.add_child(info)
|
limited.add_child(info)
|
||||||
info.add_child(Node.s32('music_id', songid))
|
info.add_child(Node.s32("music_id", songid))
|
||||||
info.add_child(Node.u8('music_type', chart))
|
info.add_child(Node.u8("music_type", chart))
|
||||||
info.add_child(Node.u8('limited', self.GAME_LIMITED_UNLOCKED))
|
info.add_child(Node.u8("limited", self.GAME_LIMITED_UNLOCKED))
|
||||||
|
|
||||||
# Event config
|
# Event config
|
||||||
event = Node.void('event')
|
event = Node.void("event")
|
||||||
game.add_child(event)
|
game.add_child(event)
|
||||||
|
|
||||||
def enable_event(eid: int) -> None:
|
def enable_event(eid: int) -> None:
|
||||||
evt = Node.void('info')
|
evt = Node.void("info")
|
||||||
event.add_child(evt)
|
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(1) # Matching enabled
|
||||||
enable_event(2) # Floor Infection
|
enable_event(2) # Floor Infection
|
||||||
enable_event(3) # Policy Break
|
enable_event(3) # Policy Break
|
||||||
@ -194,7 +197,7 @@ class SoundVoltexGravityWars(
|
|||||||
enable_event(eventid)
|
enable_event(eventid)
|
||||||
|
|
||||||
# Skill Analyzer config
|
# Skill Analyzer config
|
||||||
skill_course = Node.void('skill_course')
|
skill_course = Node.void("skill_course")
|
||||||
game.add_child(skill_course)
|
game.add_child(skill_course)
|
||||||
|
|
||||||
seasons = self._get_skill_analyzer_seasons()
|
seasons = self._get_skill_analyzer_seasons()
|
||||||
@ -202,74 +205,91 @@ class SoundVoltexGravityWars(
|
|||||||
courses = self._get_skill_analyzer_courses()
|
courses = self._get_skill_analyzer_courses()
|
||||||
max_level: Dict[int, int] = {}
|
max_level: Dict[int, int] = {}
|
||||||
for course in courses:
|
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:
|
for course in courses:
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
skill_course.add_child(info)
|
skill_course.add_child(info)
|
||||||
info.add_child(Node.s16('course_id', course.get('id', course['level'])))
|
info.add_child(Node.s16("course_id", course.get("id", course["level"])))
|
||||||
info.add_child(Node.s16('level', course['level']))
|
info.add_child(Node.s16("level", course["level"]))
|
||||||
info.add_child(Node.s32('season_id', course['season_id']))
|
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.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(
|
||||||
info.add_child(Node.string('course_name', course.get('skill_name', skillnames.get(course['level'], ''))))
|
Node.bool(
|
||||||
info.add_child(Node.s16('course_type', 0))
|
"season_new_flg", max_level[course["level"]] == course["season_id"]
|
||||||
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(
|
||||||
info.add_child(Node.s16('paseli_type', 0))
|
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']):
|
for trackno, trackdata in enumerate(course["tracks"]):
|
||||||
track = Node.void('track')
|
track = Node.void("track")
|
||||||
info.add_child(track)
|
info.add_child(track)
|
||||||
track.add_child(Node.s16('track_no', trackno))
|
track.add_child(Node.s16("track_no", trackno))
|
||||||
track.add_child(Node.s32('music_id', trackdata['id']))
|
track.add_child(Node.s32("music_id", trackdata["id"]))
|
||||||
track.add_child(Node.s8('music_type', trackdata['type']))
|
track.add_child(Node.s8("music_type", trackdata["type"]))
|
||||||
|
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_exception_request(self, request: Node) -> Node:
|
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:
|
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
|
# Respond with number of milliseconds until next request
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
game.add_child(Node.u32('nxt_time', 1000 * 5 * 60))
|
game.add_child(Node.u32("nxt_time", 1000 * 5 * 60))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
def handle_game_3_lounge_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
# Refresh interval in seconds.
|
# Refresh interval in seconds.
|
||||||
game.add_child(Node.u32('interval', 10))
|
game.add_child(Node.u32("interval", 10))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_entry_s_request(self, request: Node) -> Node:
|
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.
|
# 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
|
return game
|
||||||
|
|
||||||
def handle_game_3_entry_e_request(self, request: Node) -> Node:
|
def handle_game_3_entry_e_request(self, request: Node) -> Node:
|
||||||
# Lobby destroy method, eid node (u32) should be used
|
# Lobby destroy method, eid node (u32) should be used
|
||||||
# to destroy any open lobbies.
|
# 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:
|
def handle_game_3_frozen_request(self, request: Node) -> Node:
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
game.add_child(Node.u8('result', 0))
|
game.add_child(Node.u8("result", 0))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_save_e_request(self, request: Node) -> Node:
|
def handle_game_3_save_e_request(self, request: Node) -> Node:
|
||||||
# This has to do with Policy Break against ReflecBeat and
|
# This has to do with Policy Break against ReflecBeat and
|
||||||
# floor infection, but we don't implement multi-game support so meh.
|
# 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:
|
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:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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:
|
if userid is not None and profile is not None:
|
||||||
# Look up packets and blocks
|
# Look up packets and blocks
|
||||||
packet = profile.get_int('packet')
|
packet = profile.get_int("packet")
|
||||||
block = profile.get_int('block')
|
block = profile.get_int("block")
|
||||||
|
|
||||||
# Add on any additional we earned this round
|
# Add on any additional we earned this round
|
||||||
packet = packet + (request.child_value('earned_gamecoin_packet') or 0)
|
packet = packet + (request.child_value("earned_gamecoin_packet") or 0)
|
||||||
block = block + (request.child_value('earned_gamecoin_block') or 0)
|
block = block + (request.child_value("earned_gamecoin_block") or 0)
|
||||||
|
|
||||||
currency_type = request.child_value('currency_type')
|
currency_type = request.child_value("currency_type")
|
||||||
price = request.child_value('item/price')
|
price = request.child_value("item/price")
|
||||||
if isinstance(price, list):
|
if isinstance(price, list):
|
||||||
# Sometimes we end up buying more than one item at once
|
# Sometimes we end up buying more than one item at once
|
||||||
price = sum(price)
|
price = sum(price)
|
||||||
@ -318,14 +338,14 @@ class SoundVoltexGravityWars(
|
|||||||
|
|
||||||
if result == 0:
|
if result == 0:
|
||||||
# Transaction is valid, update the profile with new packets and blocks
|
# Transaction is valid, update the profile with new packets and blocks
|
||||||
profile.replace_int('packet', packet)
|
profile.replace_int("packet", packet)
|
||||||
profile.replace_int('block', block)
|
profile.replace_int("block", block)
|
||||||
self.put_profile(userid, profile)
|
self.put_profile(userid, profile)
|
||||||
|
|
||||||
# If this was a song unlock, we should mark it as unlocked
|
# If this was a song unlock, we should mark it as unlocked
|
||||||
item_type = request.child_value('item/item_type')
|
item_type = request.child_value("item/item_type")
|
||||||
item_id = request.child_value('item/item_id')
|
item_id = request.child_value("item/item_id")
|
||||||
param = request.child_value('item/param')
|
param = request.child_value("item/param")
|
||||||
|
|
||||||
if not isinstance(item_type, list):
|
if not isinstance(item_type, list):
|
||||||
# Sometimes we buy multiple things at once. Make it easier by always assuming this.
|
# Sometimes we buy multiple things at once. Make it easier by always assuming this.
|
||||||
@ -339,9 +359,9 @@ class SoundVoltexGravityWars(
|
|||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
item_id[i],
|
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
|
block = 0
|
||||||
result = 1
|
result = 1
|
||||||
|
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
game.add_child(Node.u32('gamecoin_packet', packet))
|
game.add_child(Node.u32("gamecoin_packet", packet))
|
||||||
game.add_child(Node.u32('gamecoin_block', block))
|
game.add_child(Node.u32("gamecoin_block", block))
|
||||||
game.add_child(Node.s8('result', result))
|
game.add_child(Node.s8("result", result))
|
||||||
return game
|
return game
|
||||||
|
|
||||||
def handle_game_3_new_request(self, request: Node) -> Node:
|
def handle_game_3_new_request(self, request: Node) -> Node:
|
||||||
refid = request.child_value('refid')
|
refid = request.child_value("refid")
|
||||||
name = request.child_value('name')
|
name = request.child_value("name")
|
||||||
loc = ID.parse_machine_id(request.child_value('locid'))
|
loc = ID.parse_machine_id(request.child_value("locid"))
|
||||||
self.new_profile_by_refid(refid, name, loc)
|
self.new_profile_by_refid(refid, name, loc)
|
||||||
|
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_3_load_request(self, request: Node) -> Node:
|
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)
|
root = self.get_profile_by_refid(refid)
|
||||||
if root is not None:
|
if root is not None:
|
||||||
return root
|
return root
|
||||||
@ -386,17 +406,17 @@ class SoundVoltexGravityWars(
|
|||||||
profile = None
|
profile = None
|
||||||
|
|
||||||
if profile is not None:
|
if profile is not None:
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
root.add_child(Node.u8('result', 2))
|
root.add_child(Node.u8("result", 2))
|
||||||
root.add_child(Node.string('name', profile.get_str('name')))
|
root.add_child(Node.string("name", profile.get_str("name")))
|
||||||
return root
|
return root
|
||||||
else:
|
else:
|
||||||
root = Node.void('game_3')
|
root = Node.void("game_3")
|
||||||
root.add_child(Node.u8('result', 1))
|
root.add_child(Node.u8("result", 1))
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def handle_game_3_save_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
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:
|
if userid is not None and newprofile is not None:
|
||||||
self.put_profile(userid, newprofile)
|
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:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -428,28 +448,37 @@ class SoundVoltexGravityWars(
|
|||||||
scores = []
|
scores = []
|
||||||
|
|
||||||
# Output to the game
|
# Output to the game
|
||||||
game = Node.void('game_3')
|
game = Node.void("game_3")
|
||||||
new = Node.void('new')
|
new = Node.void("new")
|
||||||
game.add_child(new)
|
game.add_child(new)
|
||||||
|
|
||||||
for score in scores:
|
for score in scores:
|
||||||
music = Node.void('music')
|
music = Node.void("music")
|
||||||
new.add_child(music)
|
new.add_child(music)
|
||||||
music.add_child(Node.u32('music_id', score.id))
|
music.add_child(Node.u32("music_id", score.id))
|
||||||
music.add_child(Node.u32('music_type', score.chart))
|
music.add_child(Node.u32("music_type", score.chart))
|
||||||
music.add_child(Node.u32('score', score.points))
|
music.add_child(Node.u32("score", score.points))
|
||||||
music.add_child(Node.u32('cnt', score.plays))
|
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(
|
||||||
music.add_child(Node.u32('score_grade', self.__db_to_game_grade(score.data.get_int('grade'))))
|
Node.u32(
|
||||||
stats = score.data.get_dict('stats')
|
"clear_type",
|
||||||
music.add_child(Node.u32('btn_rate', stats.get_int('btn_rate')))
|
self.__db_to_game_clear_type(score.data.get_int("clear_type")),
|
||||||
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(
|
||||||
|
"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
|
return game
|
||||||
|
|
||||||
def handle_game_3_save_m_request(self, request: Node) -> Node:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -457,19 +486,19 @@ class SoundVoltexGravityWars(
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
# Doesn't matter if userid is None here, that's an anonymous score
|
# Doesn't matter if userid is None here, that's an anonymous score
|
||||||
musicid = request.child_value('music_id')
|
musicid = request.child_value("music_id")
|
||||||
chart = request.child_value('music_type')
|
chart = request.child_value("music_type")
|
||||||
points = request.child_value('score')
|
points = request.child_value("score")
|
||||||
combo = request.child_value('max_chain')
|
combo = request.child_value("max_chain")
|
||||||
clear_type = self.__game_to_db_clear_type(request.child_value('clear_type'))
|
clear_type = self.__game_to_db_clear_type(request.child_value("clear_type"))
|
||||||
grade = self.__game_to_db_grade(request.child_value('score_grade'))
|
grade = self.__game_to_db_grade(request.child_value("score_grade"))
|
||||||
stats = {
|
stats = {
|
||||||
'btn_rate': request.child_value('btn_rate'),
|
"btn_rate": request.child_value("btn_rate"),
|
||||||
'long_rate': request.child_value('long_rate'),
|
"long_rate": request.child_value("long_rate"),
|
||||||
'vol_rate': request.child_value('vol_rate'),
|
"vol_rate": request.child_value("vol_rate"),
|
||||||
'critical': request.child_value('critical'),
|
"critical": request.child_value("critical"),
|
||||||
'near': request.child_value('near'),
|
"near": request.child_value("near"),
|
||||||
'error': request.child_value('error'),
|
"error": request.child_value("error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Save the score
|
# Save the score
|
||||||
@ -485,10 +514,10 @@ class SoundVoltexGravityWars(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Return a blank response
|
# 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:
|
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:
|
if refid is not None:
|
||||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||||
@ -496,27 +525,29 @@ class SoundVoltexGravityWars(
|
|||||||
userid = None
|
userid = None
|
||||||
|
|
||||||
if userid is not None:
|
if userid is not None:
|
||||||
course_id = request.child_value('crsid')
|
course_id = request.child_value("crsid")
|
||||||
clear_type = request.child_value('ct')
|
clear_type = request.child_value("ct")
|
||||||
achievement_rate = request.child_value('ar')
|
achievement_rate = request.child_value("ar")
|
||||||
season_id = request.child_value('ssnid')
|
season_id = request.child_value("ssnid")
|
||||||
|
|
||||||
# Do not update the course achievement when old achievement rate is greater.
|
# 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')
|
old = self.data.local.user.get_achievement(
|
||||||
if old is not None and old.get_int('achievement_rate') > achievement_rate:
|
self.game, self.version, userid, (season_id * 100) + course_id, "course"
|
||||||
return Node.void('game_3')
|
)
|
||||||
|
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.data.local.user.put_achievement(
|
||||||
self.game,
|
self.game,
|
||||||
self.version,
|
self.version,
|
||||||
userid,
|
userid,
|
||||||
(season_id * 100) + course_id,
|
(season_id * 100) + course_id,
|
||||||
'course',
|
"course",
|
||||||
{
|
{
|
||||||
'clear_type': clear_type,
|
"clear_type": clear_type,
|
||||||
'achievement_rate': achievement_rate,
|
"achievement_rate": achievement_rate,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return a blank response
|
# 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_BAD_PIN: Final[int] = 116
|
||||||
CARD_NOT_ALLOWED: Final[int] = 110
|
CARD_NOT_ALLOWED: Final[int] = 110
|
||||||
|
|
||||||
CORRECT_PASSWORD: Final[str] = '1234'
|
CORRECT_PASSWORD: Final[str] = "1234"
|
||||||
WRONG_PASSWORD: Final[str] = '4321'
|
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.__proto = proto
|
||||||
self.pcbid = pcbid
|
self.pcbid = pcbid
|
||||||
self.config = config
|
self.config = config
|
||||||
@ -31,15 +33,15 @@ class BaseClient:
|
|||||||
return "E004" + random_hex_string(12, caps=True)
|
return "E004" + random_hex_string(12, caps=True)
|
||||||
|
|
||||||
def call_node(self) -> Node:
|
def call_node(self) -> Node:
|
||||||
call = Node.void('call')
|
call = Node.void("call")
|
||||||
call.set_attribute('model', self.config['model'])
|
call.set_attribute("model", self.config["model"])
|
||||||
call.set_attribute('srcid', self.pcbid)
|
call.set_attribute("srcid", self.pcbid)
|
||||||
call.set_attribute('tag', random_hex_string(8))
|
call.set_attribute("tag", random_hex_string(8))
|
||||||
return call
|
return call
|
||||||
|
|
||||||
def exchange(self, path: str, tree: Node) -> Node:
|
def exchange(self, path: str, tree: Node) -> Node:
|
||||||
module = tree.children[0].name
|
module = tree.children[0].name
|
||||||
method = tree.children[0].attribute('method')
|
method = tree.children[0].attribute("method")
|
||||||
|
|
||||||
return self.__proto.exchange(
|
return self.__proto.exchange(
|
||||||
f'{path}?model={self.config["model"]}&module={module}&method={method}',
|
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:
|
def __assert_path(self, root: Node, path: str) -> bool:
|
||||||
parts = path.split('/')
|
parts = path.split("/")
|
||||||
children = [root]
|
children = [root]
|
||||||
node: Optional[Node] = None
|
node: Optional[Node] = None
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if part[0] == '@':
|
if part[0] == "@":
|
||||||
# Verify attribute, should be last part in chain so
|
# Verify attribute, should be last part in chain so
|
||||||
# assume its the first node
|
# assume its the first node
|
||||||
if node is None:
|
if node is None:
|
||||||
@ -88,77 +90,79 @@ class BaseClient:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.__assert_path(root, path):
|
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()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
services = Node.void('services')
|
services = Node.void("services")
|
||||||
call.add_child(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
|
# Some older games don't include this info
|
||||||
info = Node.void('info')
|
info = Node.void("info")
|
||||||
services.add_child(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:
|
if include_net:
|
||||||
net = Node.void('net')
|
net = Node.void("net")
|
||||||
services.add_child(net)
|
services.add_child(net)
|
||||||
iface = Node.void('if')
|
iface = Node.void("if")
|
||||||
net.add_child(iface)
|
net.add_child(iface)
|
||||||
iface.add_child(Node.u8('id', 0))
|
iface.add_child(Node.u8("id", 0))
|
||||||
iface.add_child(Node.bool('valid', True))
|
iface.add_child(Node.bool("valid", True))
|
||||||
iface.add_child(Node.u8('type', 1))
|
iface.add_child(Node.u8("type", 1))
|
||||||
iface.add_child(Node.u8_array('mac', [1, 2, 3, 4, 5, 6]))
|
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("addr", "10.0.0.100"))
|
||||||
iface.add_child(Node.ipv4('bcast', '10.0.0.255'))
|
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("netmask", "255.255.255.0"))
|
||||||
iface.add_child(Node.ipv4('gateway', '10.0.0.1'))
|
iface.add_child(Node.ipv4("gateway", "10.0.0.1"))
|
||||||
iface.add_child(Node.ipv4('dhcp', '10.0.0.1'))
|
iface.add_child(Node.ipv4("dhcp", "10.0.0.1"))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/services', call)
|
resp = self.exchange("core/services", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/services")
|
self.assert_path(resp, "response/services")
|
||||||
items = resp.child('services').children
|
items = resp.child("services").children
|
||||||
|
|
||||||
returned_services = []
|
returned_services = []
|
||||||
for item in items:
|
for item in items:
|
||||||
# Make sure it is an item with a url component
|
# 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
|
# Get list of services provided
|
||||||
returned_services.append(item.attribute('name'))
|
returned_services.append(item.attribute("name"))
|
||||||
|
|
||||||
for service in expected_services:
|
for service in expected_services:
|
||||||
if service not in returned_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:
|
def verify_pcbtracker_alive(self, ecflag: int = 1) -> bool:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
pcbtracker = Node.void('pcbtracker')
|
pcbtracker = Node.void("pcbtracker")
|
||||||
call.add_child(pcbtracker)
|
call.add_child(pcbtracker)
|
||||||
pcbtracker.set_attribute('accountid', self.pcbid)
|
pcbtracker.set_attribute("accountid", self.pcbid)
|
||||||
pcbtracker.set_attribute('ecflag', str(ecflag))
|
pcbtracker.set_attribute("ecflag", str(ecflag))
|
||||||
pcbtracker.set_attribute('hardid', '01000027584F6D3A')
|
pcbtracker.set_attribute("hardid", "01000027584F6D3A")
|
||||||
pcbtracker.set_attribute('method', 'alive')
|
pcbtracker.set_attribute("method", "alive")
|
||||||
pcbtracker.set_attribute('softid', '00010203040506070809')
|
pcbtracker.set_attribute("softid", "00010203040506070809")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/pcbtracker', call)
|
resp = self.exchange("core/pcbtracker", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/pcbtracker/@ecenable")
|
self.assert_path(resp, "response/pcbtracker/@ecenable")
|
||||||
|
|
||||||
# Print out setting
|
# Print out setting
|
||||||
enable = int(resp.child('pcbtracker').attribute('ecenable'))
|
enable = int(resp.child("pcbtracker").attribute("ecenable"))
|
||||||
if enable != 0:
|
if enable != 0:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -167,12 +171,12 @@ class BaseClient:
|
|||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
message = Node.void('message')
|
message = Node.void("message")
|
||||||
call.add_child(message)
|
call.add_child(message)
|
||||||
message.set_attribute('method', 'get')
|
message.set_attribute("method", "get")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/message', call)
|
resp = self.exchange("core/message", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/message/@status")
|
self.assert_path(resp, "response/message/@status")
|
||||||
@ -181,13 +185,13 @@ class BaseClient:
|
|||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
dlstatus = Node.void('dlstatus')
|
dlstatus = Node.void("dlstatus")
|
||||||
call.add_child(dlstatus)
|
call.add_child(dlstatus)
|
||||||
dlstatus.set_attribute('method', 'progress')
|
dlstatus.set_attribute("method", "progress")
|
||||||
dlstatus.add_child(Node.s32('progress', 0))
|
dlstatus.add_child(Node.s32("progress", 0))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/dlstatus', call)
|
resp = self.exchange("core/dlstatus", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/dlstatus/@status")
|
self.assert_path(resp, "response/dlstatus/@status")
|
||||||
@ -196,28 +200,28 @@ class BaseClient:
|
|||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
package = Node.void('package')
|
package = Node.void("package")
|
||||||
call.add_child(package)
|
call.add_child(package)
|
||||||
package.set_attribute('method', 'list')
|
package.set_attribute("method", "list")
|
||||||
package.set_attribute('pkgtype', 'all')
|
package.set_attribute("pkgtype", "all")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/package', call)
|
resp = self.exchange("core/package", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/package")
|
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()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
facility = Node.void('facility')
|
facility = Node.void("facility")
|
||||||
call.add_child(facility)
|
call.add_child(facility)
|
||||||
facility.set_attribute('encoding', encoding)
|
facility.set_attribute("encoding", encoding)
|
||||||
facility.set_attribute('method', 'get')
|
facility.set_attribute("method", "get")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/facility', call)
|
resp = self.exchange("core/facility", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/facility/location/id")
|
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/public")
|
||||||
self.assert_path(resp, "response/facility/share")
|
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:
|
def verify_pcbevent_put(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
pcbevent = Node.void('pcbevent')
|
pcbevent = Node.void("pcbevent")
|
||||||
call.add_child(pcbevent)
|
call.add_child(pcbevent)
|
||||||
pcbevent.set_attribute('method', 'put')
|
pcbevent.set_attribute("method", "put")
|
||||||
pcbevent.add_child(Node.time('time', int(time.time())))
|
pcbevent.add_child(Node.time("time", int(time.time())))
|
||||||
pcbevent.add_child(Node.u32('seq', 0))
|
pcbevent.add_child(Node.u32("seq", 0))
|
||||||
|
|
||||||
item = Node.void('item')
|
item = Node.void("item")
|
||||||
pcbevent.add_child(item)
|
pcbevent.add_child(item)
|
||||||
item.add_child(Node.string('name', 'boot'))
|
item.add_child(Node.string("name", "boot"))
|
||||||
item.add_child(Node.s32('value', 1))
|
item.add_child(Node.s32("value", 1))
|
||||||
item.add_child(Node.time('time', int(time.time())))
|
item.add_child(Node.time("time", int(time.time())))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/pcbevent', call)
|
resp = self.exchange("core/pcbevent", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/pcbevent")
|
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()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
cardmng = Node.void('cardmng')
|
cardmng = Node.void("cardmng")
|
||||||
call.add_child(cardmng)
|
call.add_child(cardmng)
|
||||||
cardmng.set_attribute('cardid', card_id)
|
cardmng.set_attribute("cardid", card_id)
|
||||||
cardmng.set_attribute('cardtype', '1')
|
cardmng.set_attribute("cardtype", "1")
|
||||||
cardmng.set_attribute('method', 'inquire')
|
cardmng.set_attribute("method", "inquire")
|
||||||
cardmng.set_attribute('update', '0')
|
cardmng.set_attribute("update", "0")
|
||||||
if msg_type == 'new' and 'old_profile_model' in self.config:
|
if msg_type == "new" and "old_profile_model" in self.config:
|
||||||
cardmng.set_attribute('model', self.config['old_profile_model'])
|
cardmng.set_attribute("model", self.config["old_profile_model"])
|
||||||
|
|
||||||
# Swap with server
|
# 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
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/cardmng/@status")
|
self.assert_path(resp, "response/cardmng/@status")
|
||||||
|
|
||||||
# Verify that we weren't found
|
# 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:
|
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
|
# Nothing to return
|
||||||
return None
|
return None
|
||||||
elif msg_type == 'new':
|
elif msg_type == "new":
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/cardmng/@refid")
|
self.assert_path(resp, "response/cardmng/@refid")
|
||||||
self.assert_path(resp, "response/cardmng/@binded")
|
self.assert_path(resp, "response/cardmng/@binded")
|
||||||
self.assert_path(resp, "response/cardmng/@newflag")
|
self.assert_path(resp, "response/cardmng/@newflag")
|
||||||
self.assert_path(resp, "response/cardmng/@ecflag")
|
self.assert_path(resp, "response/cardmng/@ecflag")
|
||||||
|
|
||||||
binded = int(resp.child('cardmng').attribute('binded'))
|
binded = int(resp.child("cardmng").attribute("binded"))
|
||||||
newflag = int(resp.child('cardmng').attribute('newflag'))
|
newflag = int(resp.child("cardmng").attribute("newflag"))
|
||||||
ecflag = int(resp.child('cardmng').attribute('ecflag'))
|
ecflag = int(resp.child("cardmng").attribute("ecflag"))
|
||||||
|
|
||||||
if binded != 0:
|
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:
|
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):
|
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 the refid
|
||||||
return resp.child('cardmng').attribute('refid')
|
return resp.child("cardmng").attribute("refid")
|
||||||
elif msg_type == 'query':
|
elif msg_type == "query":
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/cardmng/@refid")
|
self.assert_path(resp, "response/cardmng/@refid")
|
||||||
self.assert_path(resp, "response/cardmng/@binded")
|
self.assert_path(resp, "response/cardmng/@binded")
|
||||||
self.assert_path(resp, "response/cardmng/@newflag")
|
self.assert_path(resp, "response/cardmng/@newflag")
|
||||||
self.assert_path(resp, "response/cardmng/@ecflag")
|
self.assert_path(resp, "response/cardmng/@ecflag")
|
||||||
|
|
||||||
binded = int(resp.child('cardmng').attribute('binded'))
|
binded = int(resp.child("cardmng").attribute("binded"))
|
||||||
newflag = int(resp.child('cardmng').attribute('newflag'))
|
newflag = int(resp.child("cardmng").attribute("newflag"))
|
||||||
ecflag = int(resp.child('cardmng').attribute('ecflag'))
|
ecflag = int(resp.child("cardmng").attribute("ecflag"))
|
||||||
|
|
||||||
if binded != 1:
|
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:
|
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):
|
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 the refid
|
||||||
return resp.child('cardmng').attribute('refid')
|
return resp.child("cardmng").attribute("refid")
|
||||||
else:
|
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:
|
def verify_cardmng_getrefid(self, card_id: str) -> str:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
cardmng = Node.void('cardmng')
|
cardmng = Node.void("cardmng")
|
||||||
call.add_child(cardmng)
|
call.add_child(cardmng)
|
||||||
cardmng.set_attribute('cardid', card_id)
|
cardmng.set_attribute("cardid", card_id)
|
||||||
cardmng.set_attribute('cardtype', '1')
|
cardmng.set_attribute("cardtype", "1")
|
||||||
cardmng.set_attribute('method', 'getrefid')
|
cardmng.set_attribute("method", "getrefid")
|
||||||
cardmng.set_attribute('newflag', '0')
|
cardmng.set_attribute("newflag", "0")
|
||||||
cardmng.set_attribute('passwd', self.CORRECT_PASSWORD)
|
cardmng.set_attribute("passwd", self.CORRECT_PASSWORD)
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/cardmng', call)
|
resp = self.exchange("core/cardmng", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/cardmng/@refid")
|
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:
|
def verify_cardmng_authpass(self, ref_id: str, correct: bool) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
cardmng = Node.void('cardmng')
|
cardmng = Node.void("cardmng")
|
||||||
call.add_child(cardmng)
|
call.add_child(cardmng)
|
||||||
cardmng.set_attribute('method', 'authpass')
|
cardmng.set_attribute("method", "authpass")
|
||||||
cardmng.set_attribute('pass', self.CORRECT_PASSWORD if correct else self.CORRECT_PASSWORD[::-1])
|
cardmng.set_attribute(
|
||||||
cardmng.set_attribute('refid', ref_id)
|
"pass", self.CORRECT_PASSWORD if correct else self.CORRECT_PASSWORD[::-1]
|
||||||
|
)
|
||||||
|
cardmng.set_attribute("refid", ref_id)
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/cardmng', call)
|
resp = self.exchange("core/cardmng", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/cardmng/@status")
|
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):
|
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]:
|
def verify_eacoin_checkin(self, card_id: str) -> Tuple[str, int]:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
eacoin = Node.void('eacoin')
|
eacoin = Node.void("eacoin")
|
||||||
call.add_child(eacoin)
|
call.add_child(eacoin)
|
||||||
eacoin.set_attribute('method', 'checkin')
|
eacoin.set_attribute("method", "checkin")
|
||||||
eacoin.add_child(Node.string('cardtype', '1'))
|
eacoin.add_child(Node.string("cardtype", "1"))
|
||||||
eacoin.add_child(Node.string('cardid', card_id))
|
eacoin.add_child(Node.string("cardid", card_id))
|
||||||
eacoin.add_child(Node.string('passwd', self.CORRECT_PASSWORD))
|
eacoin.add_child(Node.string("passwd", self.CORRECT_PASSWORD))
|
||||||
eacoin.add_child(Node.string('ectype', '1'))
|
eacoin.add_child(Node.string("ectype", "1"))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/eacoin', call)
|
resp = self.exchange("core/eacoin", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/eacoin/sessid")
|
self.assert_path(resp, "response/eacoin/sessid")
|
||||||
self.assert_path(resp, "response/eacoin/balance")
|
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:
|
def verify_eacoin_consume(self, sessid: str, balance: int, amount: int) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
eacoin = Node.void('eacoin')
|
eacoin = Node.void("eacoin")
|
||||||
call.add_child(eacoin)
|
call.add_child(eacoin)
|
||||||
eacoin.set_attribute('method', 'consume')
|
eacoin.set_attribute("method", "consume")
|
||||||
eacoin.add_child(Node.string('sessid', sessid))
|
eacoin.add_child(Node.string("sessid", sessid))
|
||||||
eacoin.add_child(Node.s16('sequence', 0))
|
eacoin.add_child(Node.s16("sequence", 0))
|
||||||
eacoin.add_child(Node.s32('payment', amount))
|
eacoin.add_child(Node.s32("payment", amount))
|
||||||
eacoin.add_child(Node.s32('service', 0))
|
eacoin.add_child(Node.s32("service", 0))
|
||||||
eacoin.add_child(Node.string('itemtype', '0'))
|
eacoin.add_child(Node.string("itemtype", "0"))
|
||||||
eacoin.add_child(Node.string('detail', '/eacoin/start_pt1'))
|
eacoin.add_child(Node.string("detail", "/eacoin/start_pt1"))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/eacoin', call)
|
resp = self.exchange("core/eacoin", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/eacoin/balance")
|
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:
|
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:
|
def verify_eacoin_checkout(self, session: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
eacoin = Node.void('eacoin')
|
eacoin = Node.void("eacoin")
|
||||||
call.add_child(eacoin)
|
call.add_child(eacoin)
|
||||||
eacoin.set_attribute('method', 'checkout')
|
eacoin.set_attribute("method", "checkout")
|
||||||
eacoin.add_child(Node.string('sessid', session))
|
eacoin.add_child(Node.string("sessid", session))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('core/eacoin', call)
|
resp = self.exchange("core/eacoin", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/eacoin/@status")
|
self.assert_path(resp, "response/eacoin/@status")
|
||||||
|
|
||||||
def verify(self, cardid: Optional[str]) -> None:
|
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):
|
class TheStarBishiBashiClient(BaseClient):
|
||||||
NAME = 'TEST'
|
NAME = "TEST"
|
||||||
|
|
||||||
def verify_eventlog_write(self, location: str) -> None:
|
def verify_eventlog_write(self, location: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
eventlog = Node.void('eventlog')
|
eventlog = Node.void("eventlog")
|
||||||
call.add_child(eventlog)
|
call.add_child(eventlog)
|
||||||
eventlog.set_attribute('method', 'write')
|
eventlog.set_attribute("method", "write")
|
||||||
eventlog.add_child(Node.u32('retrycnt', 0))
|
eventlog.add_child(Node.u32("retrycnt", 0))
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
eventlog.add_child(data)
|
eventlog.add_child(data)
|
||||||
data.add_child(Node.string('eventid', 'S_PWRON'))
|
data.add_child(Node.string("eventid", "S_PWRON"))
|
||||||
data.add_child(Node.s32('eventorder', 0))
|
data.add_child(Node.s32("eventorder", 0))
|
||||||
data.add_child(Node.u64('pcbtime', int(time.time() * 1000)))
|
data.add_child(Node.u64("pcbtime", int(time.time() * 1000)))
|
||||||
data.add_child(Node.s64('gamesession', -1))
|
data.add_child(Node.s64("gamesession", -1))
|
||||||
data.add_child(Node.string('strdata1', '1.7.6'))
|
data.add_child(Node.string("strdata1", "1.7.6"))
|
||||||
data.add_child(Node.string('strdata2', ''))
|
data.add_child(Node.string("strdata2", ""))
|
||||||
data.add_child(Node.s64('numdata1', 1))
|
data.add_child(Node.s64("numdata1", 1))
|
||||||
data.add_child(Node.s64('numdata2', 0))
|
data.add_child(Node.s64("numdata2", 0))
|
||||||
data.add_child(Node.string('locationid', location))
|
data.add_child(Node.string("locationid", location))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/eventlog/gamesession")
|
self.assert_path(resp, "response/eventlog/gamesession")
|
||||||
@ -42,17 +42,17 @@ class TheStarBishiBashiClient(BaseClient):
|
|||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
system = Node.void('system')
|
system = Node.void("system")
|
||||||
call.add_child(system)
|
call.add_child(system)
|
||||||
system.set_attribute('method', 'getmaster')
|
system.set_attribute("method", "getmaster")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
system.add_child(data)
|
system.add_child(data)
|
||||||
data.add_child(Node.string('gamekind', 'IBB'))
|
data.add_child(Node.string("gamekind", "IBB"))
|
||||||
data.add_child(Node.string('datatype', 'S_SRVMSG'))
|
data.add_child(Node.string("datatype", "S_SRVMSG"))
|
||||||
data.add_child(Node.string('datakey', 'INFO'))
|
data.add_child(Node.string("datakey", "INFO"))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/system/result")
|
self.assert_path(resp, "response/system/result")
|
||||||
@ -62,116 +62,116 @@ class TheStarBishiBashiClient(BaseClient):
|
|||||||
|
|
||||||
# Set up profile write
|
# Set up profile write
|
||||||
profiledata = [
|
profiledata = [
|
||||||
b'ffffffff',
|
b"ffffffff",
|
||||||
b'IBBDAT00',
|
b"IBBDAT00",
|
||||||
b'1',
|
b"1",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'e474c1b',
|
b"e474c1b",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'ff',
|
b"ff",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
b"0",
|
||||||
b'0',
|
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'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"",
|
||||||
b'',
|
b"",
|
||||||
b'\x96\xa2\x90\xdd\x92\xe8',
|
b"\x96\xa2\x90\xdd\x92\xe8",
|
||||||
b'\x8d\x81\x8d`',
|
b"\x8d\x81\x8d`",
|
||||||
b'',
|
b"",
|
||||||
b'',
|
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.
|
# New profile gets blank name, because we save over it at the end of the round.
|
||||||
profiledata[27] = b''
|
profiledata[27] = b""
|
||||||
elif msg_type == 'existing':
|
elif msg_type == "existing":
|
||||||
# Exiting profile gets our hardcoded name saved.
|
# Exiting profile gets our hardcoded name saved.
|
||||||
profiledata[27] = self.NAME.encode('shift-jis')
|
profiledata[27] = self.NAME.encode("shift-jis")
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
playerdata = Node.void('playerdata')
|
playerdata = Node.void("playerdata")
|
||||||
call.add_child(playerdata)
|
call.add_child(playerdata)
|
||||||
playerdata.set_attribute('method', 'usergamedata_send')
|
playerdata.set_attribute("method", "usergamedata_send")
|
||||||
playerdata.add_child(Node.u32('retrycnt', 0))
|
playerdata.add_child(Node.u32("retrycnt", 0))
|
||||||
|
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
playerdata.add_child(data)
|
playerdata.add_child(data)
|
||||||
data.add_child(Node.string('eaid', ref_id))
|
data.add_child(Node.string("eaid", ref_id))
|
||||||
data.add_child(Node.string('gamekind', 'IBB'))
|
data.add_child(Node.string("gamekind", "IBB"))
|
||||||
data.add_child(Node.u32('datanum', 1))
|
data.add_child(Node.u32("datanum", 1))
|
||||||
record = Node.void('record')
|
record = Node.void("record")
|
||||||
data.add_child(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)
|
record.add_child(d)
|
||||||
d.add_child(Node.string('bin1', ''))
|
d.add_child(Node.string("bin1", ""))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
self.assert_path(resp, "response/playerdata/result")
|
self.assert_path(resp, "response/playerdata/result")
|
||||||
|
|
||||||
def verify_usergamedata_recv(self, ref_id: str) -> str:
|
def verify_usergamedata_recv(self, ref_id: str) -> str:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
|
|
||||||
# Construct node
|
# Construct node
|
||||||
playerdata = Node.void('playerdata')
|
playerdata = Node.void("playerdata")
|
||||||
call.add_child(playerdata)
|
call.add_child(playerdata)
|
||||||
playerdata.set_attribute('method', 'usergamedata_recv')
|
playerdata.set_attribute("method", "usergamedata_recv")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
playerdata.add_child(data)
|
playerdata.add_child(data)
|
||||||
data.add_child(Node.string('eaid', ref_id))
|
data.add_child(Node.string("eaid", ref_id))
|
||||||
data.add_child(Node.string('gamekind', 'IBB'))
|
data.add_child(Node.string("gamekind", "IBB"))
|
||||||
data.add_child(Node.u32('recv_num', 1))
|
data.add_child(Node.u32("recv_num", 1))
|
||||||
data.add_child(Node.string('recv_csv', 'IBBDAT00,3fffffffff'))
|
data.add_child(Node.string("recv_csv", "IBBDAT00,3fffffffff"))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
self.assert_path(resp, "response/playerdata/result")
|
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/d/bin1")
|
||||||
self.assert_path(resp, "response/playerdata/player/record_num")
|
self.assert_path(resp, "response/playerdata/player/record_num")
|
||||||
|
|
||||||
# Grab binary data, parse out name
|
# Grab binary data, parse out name
|
||||||
bindata = resp.child_value('playerdata/player/record/d')
|
bindata = resp.child_value("playerdata/player/record/d")
|
||||||
profiledata = base64.b64decode(bindata).split(b',')
|
profiledata = base64.b64decode(bindata).split(b",")
|
||||||
|
|
||||||
# We lob off the first two values in returning profile, so the name is offset by two
|
# 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:
|
def verify(self, cardid: Optional[str]) -> None:
|
||||||
# Verify boot sequence is okay
|
# Verify boot sequence is okay
|
||||||
self.verify_services_get(
|
self.verify_services_get(
|
||||||
expected_services=[
|
expected_services=[
|
||||||
'pcbtracker',
|
"pcbtracker",
|
||||||
'pcbevent',
|
"pcbevent",
|
||||||
'local',
|
"local",
|
||||||
'message',
|
"message",
|
||||||
'facility',
|
"facility",
|
||||||
'cardmng',
|
"cardmng",
|
||||||
'package',
|
"package",
|
||||||
'posevent',
|
"posevent",
|
||||||
'pkglist',
|
"pkglist",
|
||||||
'dlstatus',
|
"dlstatus",
|
||||||
'eacoin',
|
"eacoin",
|
||||||
'lobby',
|
"lobby",
|
||||||
'ntp',
|
"ntp",
|
||||||
'keepalive'
|
"keepalive",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
paseli_enabled = self.verify_pcbtracker_alive()
|
paseli_enabled = self.verify_pcbtracker_alive()
|
||||||
@ -190,33 +190,43 @@ class TheStarBishiBashiClient(BaseClient):
|
|||||||
print(f"Generated random card ID {card} for use.")
|
print(f"Generated random card ID {card} for use.")
|
||||||
|
|
||||||
if cardid is None:
|
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)
|
ref_id = self.verify_cardmng_getrefid(card)
|
||||||
if len(ref_id) != 16:
|
if len(ref_id) != 16:
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when registering card')
|
raise Exception(
|
||||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='new', paseli_enabled=paseli_enabled):
|
f"Invalid refid '{ref_id}' returned when registering card"
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying 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
|
# 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:
|
else:
|
||||||
print("Skipping new card checks for existing card")
|
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
|
# Verify pin handling and return card handling
|
||||||
self.verify_cardmng_authpass(ref_id, correct=True)
|
self.verify_cardmng_authpass(ref_id, correct=True)
|
||||||
self.verify_cardmng_authpass(ref_id, correct=False)
|
self.verify_cardmng_authpass(ref_id, correct=False)
|
||||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled):
|
if ref_id != self.verify_cardmng_inquire(
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
card, msg_type="query", paseli_enabled=paseli_enabled
|
||||||
|
):
|
||||||
|
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
|
||||||
|
|
||||||
if cardid is None:
|
if cardid is None:
|
||||||
# Verify profile saving
|
# Verify profile saving
|
||||||
name = self.verify_usergamedata_recv(ref_id)
|
name = self.verify_usergamedata_recv(ref_id)
|
||||||
if name != '':
|
if name != "":
|
||||||
raise Exception('New profile has a name associated with it!')
|
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)
|
name = self.verify_usergamedata_recv(ref_id)
|
||||||
if name != self.NAME:
|
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:
|
else:
|
||||||
print("Skipping score checks for existing card")
|
print("Skipping score checks for existing card")
|
||||||
|
@ -3,7 +3,7 @@ import random
|
|||||||
|
|
||||||
def random_hex_string(length: int, caps: bool = False) -> str:
|
def random_hex_string(length: int, caps: bool = False) -> str:
|
||||||
if caps:
|
if caps:
|
||||||
string = '0123456789ABCDEF'
|
string = "0123456789ABCDEF"
|
||||||
else:
|
else:
|
||||||
string = '0123456789abcdef'
|
string = "0123456789abcdef"
|
||||||
return ''.join([random.choice(string) for x in range(length)])
|
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):
|
class DDR2014Client(BaseClient):
|
||||||
NAME = 'TEST'
|
NAME = "TEST"
|
||||||
|
|
||||||
def verify_game_shop(self, loc: str) -> None:
|
def verify_game_shop(self, loc: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'shop')
|
game.set_attribute("method", "shop")
|
||||||
game.set_attribute('area', '51')
|
game.set_attribute("area", "51")
|
||||||
game.set_attribute('boot', '34')
|
game.set_attribute("boot", "34")
|
||||||
game.set_attribute('close', '0')
|
game.set_attribute("close", "0")
|
||||||
game.set_attribute('close_t', '0')
|
game.set_attribute("close_t", "0")
|
||||||
game.set_attribute('coin', '02.01.--.--.01.G')
|
game.set_attribute("coin", "02.01.--.--.01.G")
|
||||||
game.set_attribute('diff', '3')
|
game.set_attribute("diff", "3")
|
||||||
game.set_attribute('during', '1')
|
game.set_attribute("during", "1")
|
||||||
game.set_attribute('edit_cnt', '0')
|
game.set_attribute("edit_cnt", "0")
|
||||||
game.set_attribute('edit_used', '1')
|
game.set_attribute("edit_used", "1")
|
||||||
game.set_attribute('first', '1')
|
game.set_attribute("first", "1")
|
||||||
game.set_attribute('ip', '1.5.7.3')
|
game.set_attribute("ip", "1.5.7.3")
|
||||||
game.set_attribute('is_freefirstplay', '1')
|
game.set_attribute("is_freefirstplay", "1")
|
||||||
game.set_attribute('is_paseli', '1')
|
game.set_attribute("is_paseli", "1")
|
||||||
game.set_attribute('loc', loc)
|
game.set_attribute("loc", loc)
|
||||||
game.set_attribute('mac', '00:11:22:33:44:55')
|
game.set_attribute("mac", "00:11:22:33:44:55")
|
||||||
game.set_attribute('machine', '2')
|
game.set_attribute("machine", "2")
|
||||||
game.set_attribute('name', 'TEST')
|
game.set_attribute("name", "TEST")
|
||||||
game.set_attribute('pay', '0')
|
game.set_attribute("pay", "0")
|
||||||
game.set_attribute('region', '.')
|
game.set_attribute("region", ".")
|
||||||
game.set_attribute('soft', self.config['model'])
|
game.set_attribute("soft", self.config["model"])
|
||||||
game.set_attribute('softid', self.pcbid)
|
game.set_attribute("softid", self.pcbid)
|
||||||
game.set_attribute('stage', '1')
|
game.set_attribute("stage", "1")
|
||||||
game.set_attribute('time', '60')
|
game.set_attribute("time", "60")
|
||||||
game.set_attribute('type', '0')
|
game.set_attribute("type", "0")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game/@stop")
|
self.assert_path(resp, "response/game/@stop")
|
||||||
|
|
||||||
def verify_game_common(self) -> None:
|
def verify_game_common(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'common')
|
game.set_attribute("method", "common")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game/flag/@id")
|
self.assert_path(resp, "response/game/flag/@id")
|
||||||
@ -67,159 +67,159 @@ class DDR2014Client(BaseClient):
|
|||||||
|
|
||||||
def verify_game_hiscore(self) -> None:
|
def verify_game_hiscore(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'hiscore')
|
game.set_attribute("method", "hiscore")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
for child in resp.child('game').children:
|
for child in resp.child("game").children:
|
||||||
self.assert_path(child, 'music/@reclink_num')
|
self.assert_path(child, "music/@reclink_num")
|
||||||
self.assert_path(child, 'music/type/@diff')
|
self.assert_path(child, "music/type/@diff")
|
||||||
self.assert_path(child, 'music/type/name')
|
self.assert_path(child, "music/type/name")
|
||||||
self.assert_path(child, 'music/type/score')
|
self.assert_path(child, "music/type/score")
|
||||||
self.assert_path(child, 'music/type/area')
|
self.assert_path(child, "music/type/area")
|
||||||
self.assert_path(child, 'music/type/rank')
|
self.assert_path(child, "music/type/rank")
|
||||||
self.assert_path(child, 'music/type/combo_type')
|
self.assert_path(child, "music/type/combo_type")
|
||||||
self.assert_path(child, 'music/type/code')
|
self.assert_path(child, "music/type/code")
|
||||||
|
|
||||||
def verify_game_area_hiscore(self) -> None:
|
def verify_game_area_hiscore(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'area_hiscore')
|
game.set_attribute("method", "area_hiscore")
|
||||||
game.set_attribute('shop_area', '51')
|
game.set_attribute("shop_area", "51")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
for child in resp.child('game').children:
|
for child in resp.child("game").children:
|
||||||
self.assert_path(child, 'music/@reclink_num')
|
self.assert_path(child, "music/@reclink_num")
|
||||||
self.assert_path(child, 'music/type/@diff')
|
self.assert_path(child, "music/type/@diff")
|
||||||
self.assert_path(child, 'music/type/name')
|
self.assert_path(child, "music/type/name")
|
||||||
self.assert_path(child, 'music/type/score')
|
self.assert_path(child, "music/type/score")
|
||||||
self.assert_path(child, 'music/type/area')
|
self.assert_path(child, "music/type/area")
|
||||||
self.assert_path(child, 'music/type/rank')
|
self.assert_path(child, "music/type/rank")
|
||||||
self.assert_path(child, 'music/type/combo_type')
|
self.assert_path(child, "music/type/combo_type")
|
||||||
self.assert_path(child, 'music/type/code')
|
self.assert_path(child, "music/type/code")
|
||||||
|
|
||||||
def verify_game_message(self) -> None:
|
def verify_game_message(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'message')
|
game.set_attribute("method", "message")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
|
|
||||||
def verify_game_ranking(self) -> None:
|
def verify_game_ranking(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'ranking')
|
game.set_attribute("method", "ranking")
|
||||||
game.set_attribute('max', '10')
|
game.set_attribute("max", "10")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
|
|
||||||
def verify_game_log(self) -> None:
|
def verify_game_log(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'log')
|
game.set_attribute("method", "log")
|
||||||
game.set_attribute('type', '0')
|
game.set_attribute("type", "0")
|
||||||
game.set_attribute('soft', self.config['model'])
|
game.set_attribute("soft", self.config["model"])
|
||||||
game.set_attribute('softid', self.pcbid)
|
game.set_attribute("softid", self.pcbid)
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('boot', '34')
|
game.set_attribute("boot", "34")
|
||||||
game.set_attribute('mac', '00:11:22:33:44:55')
|
game.set_attribute("mac", "00:11:22:33:44:55")
|
||||||
clear = Node.void('clear')
|
clear = Node.void("clear")
|
||||||
game.add_child(clear)
|
game.add_child(clear)
|
||||||
clear.set_attribute('book', '0')
|
clear.set_attribute("book", "0")
|
||||||
clear.set_attribute('edit', '0')
|
clear.set_attribute("edit", "0")
|
||||||
clear.set_attribute('rank', '0')
|
clear.set_attribute("rank", "0")
|
||||||
clear.set_attribute('set', '0')
|
clear.set_attribute("set", "0")
|
||||||
auto = Node.void('auto')
|
auto = Node.void("auto")
|
||||||
game.add_child(auto)
|
game.add_child(auto)
|
||||||
auto.set_attribute('book', '1')
|
auto.set_attribute("book", "1")
|
||||||
auto.set_attribute('edit', '1')
|
auto.set_attribute("edit", "1")
|
||||||
auto.set_attribute('rank', '1')
|
auto.set_attribute("rank", "1")
|
||||||
auto.set_attribute('set', '1')
|
auto.set_attribute("set", "1")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
|
|
||||||
def verify_game_tax_info(self) -> None:
|
def verify_game_tax_info(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'tax_info')
|
game.set_attribute("method", "tax_info")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game/tax_info/@tax_phase")
|
self.assert_path(resp, "response/game/tax_info/@tax_phase")
|
||||||
|
|
||||||
def verify_game_recorder(self) -> None:
|
def verify_game_recorder(self) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'recorder')
|
game.set_attribute("method", "recorder")
|
||||||
game.set_attribute('assert_cnt', '0')
|
game.set_attribute("assert_cnt", "0")
|
||||||
game.set_attribute('assert_info', '')
|
game.set_attribute("assert_info", "")
|
||||||
game.set_attribute('assert_path', '')
|
game.set_attribute("assert_path", "")
|
||||||
game.set_attribute('assert_time', '0')
|
game.set_attribute("assert_time", "0")
|
||||||
game.set_attribute('boot_time', '1706151228')
|
game.set_attribute("boot_time", "1706151228")
|
||||||
game.set_attribute('cnt_demo', '1')
|
game.set_attribute("cnt_demo", "1")
|
||||||
game.set_attribute('cnt_music', '1')
|
game.set_attribute("cnt_music", "1")
|
||||||
game.set_attribute('cnt_play', '0')
|
game.set_attribute("cnt_play", "0")
|
||||||
game.set_attribute('last_mid', '481')
|
game.set_attribute("last_mid", "481")
|
||||||
game.set_attribute('last_seq', '36')
|
game.set_attribute("last_seq", "36")
|
||||||
game.set_attribute('last_step', '0')
|
game.set_attribute("last_step", "0")
|
||||||
game.set_attribute('last_time', '1706151235')
|
game.set_attribute("last_time", "1706151235")
|
||||||
game.set_attribute('softcode', self.config['model'])
|
game.set_attribute("softcode", self.config["model"])
|
||||||
game.set_attribute('temp_seq', '15')
|
game.set_attribute("temp_seq", "15")
|
||||||
game.set_attribute('temp_step', '8')
|
game.set_attribute("temp_step", "8")
|
||||||
game.set_attribute('temp_time', '1706151234')
|
game.set_attribute("temp_time", "1706151234")
|
||||||
game.set_attribute('wd_restart', '0')
|
game.set_attribute("wd_restart", "0")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
|
|
||||||
def verify_game_lock(self, ref_id: str, play: int) -> None:
|
def verify_game_lock(self, ref_id: str, play: int) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
game.set_attribute('method', 'lock')
|
game.set_attribute("method", "lock")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('play', str(play))
|
game.set_attribute("play", str(play))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game/@now_login")
|
self.assert_path(resp, "response/game/@now_login")
|
||||||
@ -228,34 +228,34 @@ class DDR2014Client(BaseClient):
|
|||||||
# Pad the name to 8 characters
|
# Pad the name to 8 characters
|
||||||
name = self.NAME[:8]
|
name = self.NAME[:8]
|
||||||
while len(name) < 8:
|
while len(name) < 8:
|
||||||
name = name + ' '
|
name = name + " "
|
||||||
|
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'new')
|
game.set_attribute("method", "new")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('name', name)
|
game.set_attribute("name", name)
|
||||||
game.set_attribute('area', '51')
|
game.set_attribute("area", "51")
|
||||||
game.set_attribute('old', '0')
|
game.set_attribute("old", "0")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
|
|
||||||
def verify_game_load_daily(self, ref_id: str) -> None:
|
def verify_game_load_daily(self, ref_id: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'load_daily')
|
game.set_attribute("method", "load_daily")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
|
|
||||||
# Swap with server
|
# 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/daycount/@playcount")
|
||||||
self.assert_path(resp, "response/game/dailycombo/@daily_combo")
|
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]:
|
def verify_game_load(self, ref_id: str, msg_type: str) -> Dict[str, Any]:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'load')
|
game.set_attribute("method", "load")
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
if msg_type == 'new':
|
if msg_type == "new":
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game/@none")
|
self.assert_path(resp, "response/game/@none")
|
||||||
return {}
|
return {}
|
||||||
if msg_type == 'existing':
|
if msg_type == "existing":
|
||||||
# Verify existing profile and return info
|
# Verify existing profile and return info
|
||||||
self.assert_path(resp, "response/game/seq")
|
self.assert_path(resp, "response/game/seq")
|
||||||
self.assert_path(resp, "response/game/code")
|
self.assert_path(resp, "response/game/code")
|
||||||
@ -345,63 +345,63 @@ class DDR2014Client(BaseClient):
|
|||||||
for i in range(55):
|
for i in range(55):
|
||||||
self.assert_path(resp, f"response/game/play_area/@play_cnt{i}")
|
self.assert_path(resp, f"response/game/play_area/@play_cnt{i}")
|
||||||
|
|
||||||
gr_s = resp.child('game/gr_s')
|
gr_s = resp.child("game/gr_s")
|
||||||
gr_d = resp.child('game/gr_d')
|
gr_d = resp.child("game/gr_d")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name': resp.child_value('game/name'),
|
"name": resp.child_value("game/name"),
|
||||||
'ext_id': resp.child_value('game/code'),
|
"ext_id": resp.child_value("game/code"),
|
||||||
'single_plays': resp.child_value('game/cnt_s'),
|
"single_plays": resp.child_value("game/cnt_s"),
|
||||||
'double_plays': resp.child_value('game/cnt_d'),
|
"double_plays": resp.child_value("game/cnt_d"),
|
||||||
'groove_single': [
|
"groove_single": [
|
||||||
int(gr_s.attribute('gr1')),
|
int(gr_s.attribute("gr1")),
|
||||||
int(gr_s.attribute('gr2')),
|
int(gr_s.attribute("gr2")),
|
||||||
int(gr_s.attribute('gr3')),
|
int(gr_s.attribute("gr3")),
|
||||||
int(gr_s.attribute('gr4')),
|
int(gr_s.attribute("gr4")),
|
||||||
int(gr_s.attribute('gr5')),
|
int(gr_s.attribute("gr5")),
|
||||||
],
|
],
|
||||||
'groove_double': [
|
"groove_double": [
|
||||||
int(gr_d.attribute('gr1')),
|
int(gr_d.attribute("gr1")),
|
||||||
int(gr_d.attribute('gr2')),
|
int(gr_d.attribute("gr2")),
|
||||||
int(gr_d.attribute('gr3')),
|
int(gr_d.attribute("gr3")),
|
||||||
int(gr_d.attribute('gr4')),
|
int(gr_d.attribute("gr4")),
|
||||||
int(gr_d.attribute('gr5')),
|
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]]]:
|
def verify_game_load_m(self, ref_id: str) -> Dict[int, Dict[int, Dict[str, Any]]]:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('all', '1')
|
game.set_attribute("all", "1")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
game.set_attribute('method', 'load_m')
|
game.set_attribute("method", "load_m")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
scores: Dict[int, Dict[int, Dict[str, Any]]] = {}
|
scores: Dict[int, Dict[int, Dict[str, Any]]] = {}
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
for child in resp.child('game').children:
|
for child in resp.child("game").children:
|
||||||
self.assert_path(child, 'music/@reclink')
|
self.assert_path(child, "music/@reclink")
|
||||||
reclink = int(child.attribute('reclink'))
|
reclink = int(child.attribute("reclink"))
|
||||||
|
|
||||||
for typenode in child.children:
|
for typenode in child.children:
|
||||||
self.assert_path(typenode, 'type/@diff')
|
self.assert_path(typenode, "type/@diff")
|
||||||
self.assert_path(typenode, 'type/score')
|
self.assert_path(typenode, "type/score")
|
||||||
self.assert_path(typenode, 'type/count')
|
self.assert_path(typenode, "type/count")
|
||||||
self.assert_path(typenode, 'type/rank')
|
self.assert_path(typenode, "type/rank")
|
||||||
self.assert_path(typenode, 'type/combo_type')
|
self.assert_path(typenode, "type/combo_type")
|
||||||
chart = int(typenode.attribute('diff'))
|
chart = int(typenode.attribute("diff"))
|
||||||
vals = {
|
vals = {
|
||||||
'score': typenode.child_value('score'),
|
"score": typenode.child_value("score"),
|
||||||
'count': typenode.child_value('count'),
|
"count": typenode.child_value("count"),
|
||||||
'rank': typenode.child_value('rank'),
|
"rank": typenode.child_value("rank"),
|
||||||
'halo': typenode.child_value('combo_type'),
|
"halo": typenode.child_value("combo_type"),
|
||||||
}
|
}
|
||||||
if reclink not in scores:
|
if reclink not in scores:
|
||||||
scores[reclink] = {}
|
scores[reclink] = {}
|
||||||
@ -410,91 +410,95 @@ class DDR2014Client(BaseClient):
|
|||||||
|
|
||||||
def verify_game_load_edit(self, ref_id: str) -> None:
|
def verify_game_load_edit(self, ref_id: str) -> None:
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('pid', '0')
|
game.set_attribute("pid", "0")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
game.set_attribute('method', 'load_edit')
|
game.set_attribute("method", "load_edit")
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
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]
|
gauge = gauge or [0, 0, 0, 0, 0]
|
||||||
|
|
||||||
call = self.call_node()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'save')
|
game.set_attribute("method", "save")
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
game.set_attribute('shop_area', '51')
|
game.set_attribute("shop_area", "51")
|
||||||
last = Node.void('last')
|
last = Node.void("last")
|
||||||
game.add_child(last)
|
game.add_child(last)
|
||||||
last.set_attribute('mode', '1')
|
last.set_attribute("mode", "1")
|
||||||
last.set_attribute('style', str(style))
|
last.set_attribute("style", str(style))
|
||||||
gr = Node.void('gr')
|
gr = Node.void("gr")
|
||||||
game.add_child(gr)
|
game.add_child(gr)
|
||||||
gr.set_attribute('gr1', str(gauge[0]))
|
gr.set_attribute("gr1", str(gauge[0]))
|
||||||
gr.set_attribute('gr2', str(gauge[1]))
|
gr.set_attribute("gr2", str(gauge[1]))
|
||||||
gr.set_attribute('gr3', str(gauge[2]))
|
gr.set_attribute("gr3", str(gauge[2]))
|
||||||
gr.set_attribute('gr4', str(gauge[3]))
|
gr.set_attribute("gr4", str(gauge[3]))
|
||||||
gr.set_attribute('gr5', str(gauge[4]))
|
gr.set_attribute("gr5", str(gauge[4]))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
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()
|
call = self.call_node()
|
||||||
game = Node.void('game')
|
game = Node.void("game")
|
||||||
call.add_child(game)
|
call.add_child(game)
|
||||||
game.set_attribute('method', 'save_m')
|
game.set_attribute("method", "save_m")
|
||||||
game.set_attribute('diff', '12345')
|
game.set_attribute("diff", "12345")
|
||||||
game.set_attribute('mtype', str(score['chart']))
|
game.set_attribute("mtype", str(score["chart"]))
|
||||||
game.set_attribute('mid', str(score['id']))
|
game.set_attribute("mid", str(score["id"]))
|
||||||
game.set_attribute('refid', ref_id)
|
game.set_attribute("refid", ref_id)
|
||||||
game.set_attribute('ver', '2014102700')
|
game.set_attribute("ver", "2014102700")
|
||||||
data = Node.void('data')
|
data = Node.void("data")
|
||||||
game.add_child(data)
|
game.add_child(data)
|
||||||
data.set_attribute('score', str(score['score']))
|
data.set_attribute("score", str(score["score"]))
|
||||||
data.set_attribute('rank', str(score['rank']))
|
data.set_attribute("rank", str(score["rank"]))
|
||||||
data.set_attribute('shop_area', '0')
|
data.set_attribute("shop_area", "0")
|
||||||
data.set_attribute('playmode', '1')
|
data.set_attribute("playmode", "1")
|
||||||
data.set_attribute('combo', str(score['combo']))
|
data.set_attribute("combo", str(score["combo"]))
|
||||||
data.set_attribute('phase', '1')
|
data.set_attribute("phase", "1")
|
||||||
data.set_attribute('style', '0')
|
data.set_attribute("style", "0")
|
||||||
data.set_attribute('full', '1' if score['halo'] >= 1 else '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("great_fc", "1" if score["halo"] == 1 else "0")
|
||||||
data.set_attribute('good_fc', '1' if score['halo'] == 4 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')
|
data.set_attribute("perf_fc", "1" if score["halo"] == 2 else "0")
|
||||||
gauge = Node.void('gauge')
|
gauge = Node.void("gauge")
|
||||||
game.add_child(gauge)
|
game.add_child(gauge)
|
||||||
gauge.set_attribute('life8', '0')
|
gauge.set_attribute("life8", "0")
|
||||||
gauge.set_attribute('assist', '0')
|
gauge.set_attribute("assist", "0")
|
||||||
gauge.set_attribute('risky', '0')
|
gauge.set_attribute("risky", "0")
|
||||||
gauge.set_attribute('life4', '0')
|
gauge.set_attribute("life4", "0")
|
||||||
gauge.set_attribute('hard', '0')
|
gauge.set_attribute("hard", "0")
|
||||||
player = Node.void('player')
|
player = Node.void("player")
|
||||||
game.add_child(player)
|
game.add_child(player)
|
||||||
player.set_attribute('playcnt', '123')
|
player.set_attribute("playcnt", "123")
|
||||||
player.set_attribute('code', ext_id)
|
player.set_attribute("code", ext_id)
|
||||||
option = Node.void('option_02')
|
option = Node.void("option_02")
|
||||||
game.add_child(option)
|
game.add_child(option)
|
||||||
option.set_attribute('opt02_0', '6')
|
option.set_attribute("opt02_0", "6")
|
||||||
option.set_attribute('opt02_6', '1')
|
option.set_attribute("opt02_6", "1")
|
||||||
option.set_attribute('opt02_13', '2')
|
option.set_attribute("opt02_13", "2")
|
||||||
game.add_child(Node.u8_array('trace', [0] * 512))
|
game.add_child(Node.u8_array("trace", [0] * 512))
|
||||||
game.add_child(Node.u32('size', 512))
|
game.add_child(Node.u32("size", 512))
|
||||||
|
|
||||||
# Swap with server
|
# Swap with server
|
||||||
resp = self.exchange('', call)
|
resp = self.exchange("", call)
|
||||||
|
|
||||||
# Verify that response is correct
|
# Verify that response is correct
|
||||||
self.assert_path(resp, "response/game")
|
self.assert_path(resp, "response/game")
|
||||||
@ -503,26 +507,26 @@ class DDR2014Client(BaseClient):
|
|||||||
# Verify boot sequence is okay
|
# Verify boot sequence is okay
|
||||||
self.verify_services_get(
|
self.verify_services_get(
|
||||||
expected_services=[
|
expected_services=[
|
||||||
'pcbtracker',
|
"pcbtracker",
|
||||||
'pcbevent',
|
"pcbevent",
|
||||||
'local',
|
"local",
|
||||||
'message',
|
"message",
|
||||||
'facility',
|
"facility",
|
||||||
'cardmng',
|
"cardmng",
|
||||||
'package',
|
"package",
|
||||||
'posevent',
|
"posevent",
|
||||||
'pkglist',
|
"pkglist",
|
||||||
'dlstatus',
|
"dlstatus",
|
||||||
'eacoin',
|
"eacoin",
|
||||||
'lobby',
|
"lobby",
|
||||||
'ntp',
|
"ntp",
|
||||||
'keepalive'
|
"keepalive",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
paseli_enabled = self.verify_pcbtracker_alive()
|
paseli_enabled = self.verify_pcbtracker_alive()
|
||||||
self.verify_message_get()
|
self.verify_message_get()
|
||||||
self.verify_package_list()
|
self.verify_package_list()
|
||||||
location = self.verify_facility_get('EUC_JP')
|
location = self.verify_facility_get("EUC_JP")
|
||||||
self.verify_pcbevent_put()
|
self.verify_pcbevent_put()
|
||||||
self.verify_game_recorder()
|
self.verify_game_recorder()
|
||||||
self.verify_game_tax_info()
|
self.verify_game_tax_info()
|
||||||
@ -542,24 +546,34 @@ class DDR2014Client(BaseClient):
|
|||||||
print(f"Generated random card ID {card} for use.")
|
print(f"Generated random card ID {card} for use.")
|
||||||
|
|
||||||
if cardid is None:
|
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)
|
ref_id = self.verify_cardmng_getrefid(card)
|
||||||
if len(ref_id) != 16:
|
if len(ref_id) != 16:
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when registering card')
|
raise Exception(
|
||||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='new', paseli_enabled=paseli_enabled):
|
f"Invalid refid '{ref_id}' returned when registering card"
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying 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
|
# 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)
|
self.verify_game_new(ref_id)
|
||||||
else:
|
else:
|
||||||
print("Skipping new card checks for existing card")
|
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
|
# Verify pin handling and return card handling
|
||||||
self.verify_cardmng_authpass(ref_id, correct=True)
|
self.verify_cardmng_authpass(ref_id, correct=True)
|
||||||
self.verify_cardmng_authpass(ref_id, correct=False)
|
self.verify_cardmng_authpass(ref_id, correct=False)
|
||||||
if ref_id != self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled):
|
if ref_id != self.verify_cardmng_inquire(
|
||||||
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
|
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
|
# Verify locking and unlocking profile ability
|
||||||
self.verify_game_lock(ref_id, 1)
|
self.verify_game_lock(ref_id, 1)
|
||||||
@ -567,53 +581,53 @@ class DDR2014Client(BaseClient):
|
|||||||
|
|
||||||
if cardid is None:
|
if cardid is None:
|
||||||
# Verify empty profile
|
# Verify empty profile
|
||||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||||
ext_id = str(profile['ext_id'])
|
ext_id = str(profile["ext_id"])
|
||||||
if profile['name'] != self.NAME:
|
if profile["name"] != self.NAME:
|
||||||
raise Exception('Profile has invalid name associated with it!')
|
raise Exception("Profile has invalid name associated with it!")
|
||||||
if profile['single_plays'] != 0:
|
if profile["single_plays"] != 0:
|
||||||
raise Exception('Profile has plays on single already!')
|
raise Exception("Profile has plays on single already!")
|
||||||
if profile['double_plays'] != 0:
|
if profile["double_plays"] != 0:
|
||||||
raise Exception('Profile has plays on double already!')
|
raise Exception("Profile has plays on double already!")
|
||||||
if any([g != 0 for g in profile['groove_single']]):
|
if any([g != 0 for g in profile["groove_single"]]):
|
||||||
raise Exception('Profile has single groove gauge values already!')
|
raise Exception("Profile has single groove gauge values already!")
|
||||||
if any([g != 0 for g in profile['groove_double']]):
|
if any([g != 0 for g in profile["groove_double"]]):
|
||||||
raise Exception('Profile has double groove gauge values already!')
|
raise Exception("Profile has double groove gauge values already!")
|
||||||
|
|
||||||
# Verify empty scores
|
# Verify empty scores
|
||||||
scores = self.verify_game_load_m(ref_id)
|
scores = self.verify_game_load_m(ref_id)
|
||||||
if len(scores) > 0:
|
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_edit(ref_id)
|
||||||
self.verify_game_load_daily(ref_id)
|
self.verify_game_load_daily(ref_id)
|
||||||
|
|
||||||
# Verify profile saving
|
# Verify profile saving
|
||||||
self.verify_game_save(ref_id, 0, [1, 2, 3, 4, 5])
|
self.verify_game_save(ref_id, 0, [1, 2, 3, 4, 5])
|
||||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||||
if profile['name'] != self.NAME:
|
if profile["name"] != self.NAME:
|
||||||
raise Exception('Profile has invalid name associated with it!')
|
raise Exception("Profile has invalid name associated with it!")
|
||||||
if profile['single_plays'] != 1:
|
if profile["single_plays"] != 1:
|
||||||
raise Exception('Profile has invalid plays on single!')
|
raise Exception("Profile has invalid plays on single!")
|
||||||
if profile['double_plays'] != 0:
|
if profile["double_plays"] != 0:
|
||||||
raise Exception('Profile has invalid plays on double!')
|
raise Exception("Profile has invalid plays on double!")
|
||||||
if profile['groove_single'] != [1, 2, 3, 4, 5]:
|
if profile["groove_single"] != [1, 2, 3, 4, 5]:
|
||||||
raise Exception('Profile has invalid single groove gauge values!')
|
raise Exception("Profile has invalid single groove gauge values!")
|
||||||
if any([g != 0 for g in profile['groove_double']]):
|
if any([g != 0 for g in profile["groove_double"]]):
|
||||||
raise Exception('Profile has invalid double groove gauge values!')
|
raise Exception("Profile has invalid double groove gauge values!")
|
||||||
|
|
||||||
self.verify_game_save(ref_id, 1, [5, 4, 3, 2, 1])
|
self.verify_game_save(ref_id, 1, [5, 4, 3, 2, 1])
|
||||||
profile = self.verify_game_load(ref_id, msg_type='existing')
|
profile = self.verify_game_load(ref_id, msg_type="existing")
|
||||||
if profile['name'] != self.NAME:
|
if profile["name"] != self.NAME:
|
||||||
raise Exception('Profile has invalid name associated with it!')
|
raise Exception("Profile has invalid name associated with it!")
|
||||||
if profile['single_plays'] != 1:
|
if profile["single_plays"] != 1:
|
||||||
raise Exception('Profile has invalid plays on single!')
|
raise Exception("Profile has invalid plays on single!")
|
||||||
if profile['double_plays'] != 1:
|
if profile["double_plays"] != 1:
|
||||||
raise Exception('Profile has invalid plays on double!')
|
raise Exception("Profile has invalid plays on double!")
|
||||||
if profile['groove_single'] != [1, 2, 3, 4, 5]:
|
if profile["groove_single"] != [1, 2, 3, 4, 5]:
|
||||||
raise Exception('Profile has invalid single groove gauge values!')
|
raise Exception("Profile has invalid single groove gauge values!")
|
||||||
if profile['groove_double'] != [5, 4, 3, 2, 1]:
|
if profile["groove_double"] != [5, 4, 3, 2, 1]:
|
||||||
raise Exception('Profile has invalid double groove gauge values!')
|
raise Exception("Profile has invalid double groove gauge values!")
|
||||||
|
|
||||||
# Now, write some scores and verify saving
|
# Now, write some scores and verify saving
|
||||||
for phase in [1, 2]:
|
for phase in [1, 2]:
|
||||||
@ -621,71 +635,71 @@ class DDR2014Client(BaseClient):
|
|||||||
dummyscores = [
|
dummyscores = [
|
||||||
# An okay score on a chart
|
# An okay score on a chart
|
||||||
{
|
{
|
||||||
'id': 593,
|
"id": 593,
|
||||||
'chart': 3,
|
"chart": 3,
|
||||||
'score': 800000,
|
"score": 800000,
|
||||||
'combo': 123,
|
"combo": 123,
|
||||||
'rank': 4,
|
"rank": 4,
|
||||||
'halo': 1,
|
"halo": 1,
|
||||||
},
|
},
|
||||||
# A good score on an easier chart same song
|
# A good score on an easier chart same song
|
||||||
{
|
{
|
||||||
'id': 593,
|
"id": 593,
|
||||||
'chart': 2,
|
"chart": 2,
|
||||||
'score': 990000,
|
"score": 990000,
|
||||||
'combo': 321,
|
"combo": 321,
|
||||||
'rank': 2,
|
"rank": 2,
|
||||||
'halo': 2,
|
"halo": 2,
|
||||||
},
|
},
|
||||||
# A perfect score
|
# A perfect score
|
||||||
{
|
{
|
||||||
'id': 483,
|
"id": 483,
|
||||||
'chart': 3,
|
"chart": 3,
|
||||||
'score': 1000000,
|
"score": 1000000,
|
||||||
'combo': 400,
|
"combo": 400,
|
||||||
'rank': 1,
|
"rank": 1,
|
||||||
'halo': 3,
|
"halo": 3,
|
||||||
},
|
},
|
||||||
# A bad score
|
# A bad score
|
||||||
{
|
{
|
||||||
'id': 483,
|
"id": 483,
|
||||||
'chart': 2,
|
"chart": 2,
|
||||||
'score': 100000,
|
"score": 100000,
|
||||||
'combo': 5,
|
"combo": 5,
|
||||||
'rank': 7,
|
"rank": 7,
|
||||||
'halo': 0,
|
"halo": 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 483,
|
"id": 483,
|
||||||
'chart': 1,
|
"chart": 1,
|
||||||
'score': 60000,
|
"score": 60000,
|
||||||
'combo': 5,
|
"combo": 5,
|
||||||
'rank': 6,
|
"rank": 6,
|
||||||
'halo': 4,
|
"halo": 4,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
if phase == 2:
|
if phase == 2:
|
||||||
dummyscores = [
|
dummyscores = [
|
||||||
# A better score on a chart
|
# A better score on a chart
|
||||||
{
|
{
|
||||||
'id': 593,
|
"id": 593,
|
||||||
'chart': 3,
|
"chart": 3,
|
||||||
'score': 850000,
|
"score": 850000,
|
||||||
'combo': 234,
|
"combo": 234,
|
||||||
'rank': 3,
|
"rank": 3,
|
||||||
'halo': 2,
|
"halo": 2,
|
||||||
},
|
},
|
||||||
# A worse score on another chart
|
# A worse score on another chart
|
||||||
{
|
{
|
||||||
'id': 593,
|
"id": 593,
|
||||||
'chart': 2,
|
"chart": 2,
|
||||||
'score': 980000,
|
"score": 980000,
|
||||||
'combo': 300,
|
"combo": 300,
|
||||||
'rank': 3,
|
"rank": 3,
|
||||||
'halo': 0,
|
"halo": 0,
|
||||||
'expected_score': 990000,
|
"expected_score": 990000,
|
||||||
'expected_rank': 2,
|
"expected_rank": 2,
|
||||||
'expected_halo': 2,
|
"expected_halo": 2,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -693,21 +707,29 @@ class DDR2014Client(BaseClient):
|
|||||||
self.verify_game_save_m(ref_id, ext_id, score)
|
self.verify_game_save_m(ref_id, ext_id, score)
|
||||||
scores = self.verify_game_load_m(ref_id)
|
scores = self.verify_game_load_m(ref_id)
|
||||||
for score in dummyscores:
|
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:
|
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
|
# Verify the attributes of the score
|
||||||
expected_score = score.get('expected_score', score['score'])
|
expected_score = score.get("expected_score", score["score"])
|
||||||
expected_rank = score.get('expected_rank', score['rank'])
|
expected_rank = score.get("expected_rank", score["rank"])
|
||||||
expected_halo = score.get('expected_halo', score['halo'])
|
expected_halo = score.get("expected_halo", score["halo"])
|
||||||
|
|
||||||
if data['score'] != expected_score:
|
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"]}\'')
|
raise Exception(
|
||||||
if data['rank'] != expected_rank:
|
f'Expected a score of \'{expected_score}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got score \'{data["score"]}\''
|
||||||
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:
|
if data["rank"] != expected_rank:
|
||||||
raise Exception(f'Expected a halo of \'{expected_halo}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got halo \'{data["halo"]}\'')
|
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
|
# Sleep so we don't end up putting in score history on the same second
|
||||||
time.sleep(1)
|
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