1
0
mirror of synced 2024-12-24 11:44:51 +01:00
bemaniutils/bemani/client/sdvx/infiniteinfection.py

853 lines
34 KiB
Python
Raw Normal View History

import random
import time
from typing import Any, Dict, List, Optional
from bemani.client.base import BaseClient
from bemani.protocol import Node
class SoundVoltexInfiniteInfectionClient(BaseClient):
NAME = "TEST"
def verify_eventlog_write(self, location: str) -> None:
call = self.call_node()
# Construct node
eventlog = Node.void("eventlog")
call.add_child(eventlog)
eventlog.set_attribute("method", "write")
eventlog.add_child(Node.u32("retrycnt", 0))
data = Node.void("data")
eventlog.add_child(data)
data.add_child(Node.string("eventid", "S_PWRON"))
data.add_child(Node.s32("eventorder", 0))
data.add_child(Node.u64("pcbtime", int(time.time() * 1000)))
data.add_child(Node.s64("gamesession", -1))
data.add_child(Node.string("strdata1", "1.7.6"))
data.add_child(Node.string("strdata2", ""))
data.add_child(Node.s64("numdata1", 1))
data.add_child(Node.s64("numdata2", 0))
data.add_child(Node.string("locationid", location))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/eventlog/gamesession")
self.assert_path(resp, "response/eventlog/logsendflg")
self.assert_path(resp, "response/eventlog/logerrlevel")
self.assert_path(resp, "response/eventlog/evtidnosendflg")
def verify_game_hiscore(self, location: str) -> None:
call = self.call_node()
game = Node.void("game_2")
game.set_attribute("ver", "0")
game.set_attribute("method", "hiscore")
game.add_child(Node.string("locid", location))
call.add_child(game)
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/hitchart/info/id")
self.assert_path(resp, "response/game_2/hitchart/info/cnt")
self.assert_path(resp, "response/game_2/hiscore_allover/info/id")
self.assert_path(resp, "response/game_2/hiscore_allover/info/type")
self.assert_path(resp, "response/game_2/hiscore_allover/info/name")
self.assert_path(resp, "response/game_2/hiscore_allover/info/code")
self.assert_path(resp, "response/game_2/hiscore_allover/info/score")
self.assert_path(resp, "response/game_2/hiscore_location/info/id")
self.assert_path(resp, "response/game_2/hiscore_location/info/type")
self.assert_path(resp, "response/game_2/hiscore_location/info/name")
self.assert_path(resp, "response/game_2/hiscore_location/info/code")
self.assert_path(resp, "response/game_2/hiscore_location/info/score")
self.assert_path(resp, "response/game_2/clear_rate/d/id")
self.assert_path(resp, "response/game_2/clear_rate/d/type")
self.assert_path(resp, "response/game_2/clear_rate/d/cr")
def verify_game_shop(self, location: str) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "shop")
game.set_attribute("ver", "0")
game.add_child(Node.string("locid", location))
game.add_child(Node.string("regcode", "."))
game.add_child(Node.string("locname", ""))
game.add_child(Node.u8("loctype", 0))
game.add_child(Node.string("cstcode", ""))
game.add_child(Node.string("cpycode", ""))
game.add_child(Node.s32("latde", 0))
game.add_child(Node.s32("londe", 0))
game.add_child(Node.u8("accu", 0))
game.add_child(Node.string("linid", "."))
game.add_child(Node.u8("linclass", 0))
game.add_child(Node.ipv4("ipaddr", "0.0.0.0"))
game.add_child(Node.string("hadid", "00010203040506070809"))
game.add_child(Node.string("licid", "00010203040506070809"))
game.add_child(Node.string("actid", self.pcbid))
game.add_child(Node.s8("appstate", 0))
game.add_child(Node.s8("c_need", 1))
game.add_child(Node.s8("c_credit", 2))
game.add_child(Node.s8("s_credit", 2))
game.add_child(Node.bool("free_p", True))
game.add_child(Node.bool("close", False))
game.add_child(Node.s32("close_t", 1380))
game.add_child(Node.u32("playc", 0))
game.add_child(Node.u32("playn", 0))
game.add_child(Node.u32("playe", 0))
game.add_child(Node.u32("test_m", 0))
game.add_child(Node.u32("service", 0))
game.add_child(Node.bool("paseli", True))
game.add_child(Node.u32("update", 0))
game.add_child(Node.string("shopname", ""))
game.add_child(Node.bool("newpc", False))
game.add_child(Node.s32("s_paseli", 206))
game.add_child(Node.s32("monitor", 1))
game.add_child(Node.string("romnumber", "KFC-JA-M01"))
game.add_child(Node.string("etc", "TaxMode:1,BasicRate:100/1,FirstFree:0"))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/nxt_time")
def verify_game_new(self, location: str, refid: str) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "new")
game.set_attribute("ver", "0")
game.add_child(Node.string("dataid", refid))
game.add_child(Node.string("refid", refid))
game.add_child(Node.string("name", self.NAME))
game.add_child(Node.string("locid", location))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2")
def verify_game_frozen(self, refid: str, time: int) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("ver", "0")
game.set_attribute("method", "frozen")
game.add_child(Node.string("refid", refid))
game.add_child(Node.u32("sec", time))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/result")
def verify_game_save(
self,
location: str,
refid: str,
packet: int,
block: int,
blaster_energy: int,
appealcards: List[int],
) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "save")
game.set_attribute("ver", "0")
game.add_child(Node.string("refid", refid))
game.add_child(Node.string("locid", location))
game.add_child(Node.u8("headphone", 0))
game.add_child(Node.u8("hispeed", 52))
game.add_child(Node.u16("appeal_id", 1001))
game.add_child(Node.u16("comment_id", 0))
game.add_child(Node.s32("music_id", 29))
game.add_child(Node.u8("music_type", 1))
game.add_child(Node.u8("sort_type", 1))
game.add_child(Node.u8("narrow_down", 0))
game.add_child(Node.u8("gauge_option", 0))
game.add_child(Node.u32("earned_gamecoin_packet", packet))
game.add_child(Node.u32("earned_gamecoin_block", block))
game.add_child(Node.void("item"))
appealcard = Node.void("appealcard")
game.add_child(appealcard)
for card in appealcards:
info = Node.void("info")
info.add_child(Node.u32("id", card))
info.add_child(Node.u32("count", 0))
appealcard.add_child(info)
game.add_child(
Node.s32_array(
"hidden_param",
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
)
)
game.add_child(Node.s16("skill_name_id", -1))
game.add_child(Node.s32("earned_blaster_energy", blaster_energy))
game.add_child(Node.u32("blaster_count", 0))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2")
def verify_game_common(self) -> None:
call = self.call_node()
game = Node.void("game_2")
game.set_attribute("ver", "0")
game.set_attribute("method", "common")
call.add_child(game)
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/music_limited")
self.assert_path(resp, "response/game_2/event/info/event_id")
self.assert_path(resp, "response/game_2/catalog")
self.assert_path(resp, "response/game_2/skill_course/info/course_id")
self.assert_path(resp, "response/game_2/skill_course/info/level")
self.assert_path(resp, "response/game_2/skill_course/info/season_id")
self.assert_path(resp, "response/game_2/skill_course/info/season_name")
self.assert_path(resp, "response/game_2/skill_course/info/season_new_flg")
self.assert_path(resp, "response/game_2/skill_course/info/course_name")
self.assert_path(resp, "response/game_2/skill_course/info/course_type")
self.assert_path(resp, "response/game_2/skill_course/info/skill_name_id")
self.assert_path(resp, "response/game_2/skill_course/info/matching_assist")
self.assert_path(resp, "response/game_2/skill_course/info/gauge_type")
self.assert_path(resp, "response/game_2/skill_course/info/paseli_type")
self.assert_path(resp, "response/game_2/skill_course/info/track/track_no")
self.assert_path(resp, "response/game_2/skill_course/info/track/music_id")
self.assert_path(resp, "response/game_2/skill_course/info/track/music_type")
def verify_game_buy(
self,
refid: str,
catalogtype: int,
catalogid: int,
currencytype: int,
price: int,
itemtype: int,
itemid: int,
param: int,
success: bool,
) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("ver", "0")
game.set_attribute("method", "buy")
game.add_child(Node.string("refid", refid))
game.add_child(Node.u8("catalog_type", catalogtype))
game.add_child(Node.u32("catalog_id", catalogid))
game.add_child(Node.u32("earned_gamecoin_packet", 0))
game.add_child(Node.u32("earned_gamecoin_block", 0))
game.add_child(Node.u32("currency_type", currencytype))
game.add_child(Node.u32("price", price))
game.add_child(Node.u32("item_type", itemtype))
game.add_child(Node.u32("item_id", itemid))
game.add_child(Node.u32("param", param))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/gamecoin_packet")
self.assert_path(resp, "response/game_2/gamecoin_block")
self.assert_path(resp, "response/game_2/result")
if success:
if resp.child_value("game_2/result") != 0:
raise Exception("Failed to purchase!")
else:
if resp.child_value("game_2/result") == 0:
raise Exception("Purchased when shouldn't have!")
def verify_game_load(
self, cardid: str, refid: str, msg_type: str
) -> Dict[str, Any]:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "load")
game.set_attribute("ver", "0")
game.add_child(Node.string("dataid", refid))
game.add_child(Node.string("cardid", cardid))
game.add_child(Node.string("refid", refid))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
if msg_type == "new":
self.assert_path(resp, "response/game_2/result")
if resp.child_value("game_2/result") != 1:
raise Exception("Invalid result for new profile!")
return None
if msg_type == "existing":
self.assert_path(resp, "response/game_2/name")
self.assert_path(resp, "response/game_2/code")
self.assert_path(resp, "response/game_2/gamecoin_packet")
self.assert_path(resp, "response/game_2/gamecoin_block")
self.assert_path(resp, "response/game_2/skill_name_id")
self.assert_path(resp, "response/game_2/hidden_param")
self.assert_path(resp, "response/game_2/blaster_energy")
self.assert_path(resp, "response/game_2/blaster_count")
self.assert_path(resp, "response/game_2/play_count")
self.assert_path(resp, "response/game_2/daily_count")
self.assert_path(resp, "response/game_2/play_chain")
self.assert_path(resp, "response/game_2/item")
self.assert_path(resp, "response/game_2/appealcard")
self.assert_path(resp, "response/game_2/skill/course_all")
items: Dict[int, Dict[int, int]] = {}
for child in resp.child("game_2/item").children:
if child.name != "info":
continue
itype = child.child_value("type")
iid = child.child_value("id")
param = child.child_value("param")
if itype not in items:
items[itype] = {}
items[itype][iid] = param
appealcards: Dict[int, int] = {}
for child in resp.child("game_2/appealcard").children:
if child.name != "info":
continue
iid = child.child_value("id")
count = child.child_value("count")
appealcards[iid] = count
courses: Dict[int, Dict[int, Dict[str, int]]] = {}
for child in resp.child("game_2/skill/course_all").children:
if child.name != "d":
continue
crsid = child.child_value("crsid")
season = child.child_value("ssnid")
achievement_rate = child.child_value("ar")
clear_type = child.child_value("ct")
if season not in courses:
courses[season] = {}
courses[season][crsid] = {
"achievement_rate": achievement_rate,
"clear_type": clear_type,
}
return {
"name": resp.child_value("game_2/name"),
"packet": resp.child_value("game_2/gamecoin_packet"),
"block": resp.child_value("game_2/gamecoin_block"),
"blaster_energy": resp.child_value("game_2/blaster_energy"),
"items": items,
"appealcards": appealcards,
"courses": courses,
}
else:
raise Exception(f"Invalid game load type {msg_type}")
def verify_game_lounge(self) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "lounge")
game.set_attribute("ver", "0")
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/interval")
def verify_game_entry_s(self) -> int:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("ver", "0")
game.set_attribute("method", "entry_s")
game.add_child(Node.u8("c_ver", 69))
game.add_child(Node.u8("p_num", 1))
game.add_child(Node.u8("p_rest", 1))
game.add_child(Node.u8("filter", 1))
game.add_child(Node.u32("mid", 416))
game.add_child(Node.u32("sec", 45))
game.add_child(Node.u16("port", 10007))
game.add_child(Node.fouru8("gip", [127, 0, 0, 1]))
game.add_child(Node.fouru8("lip", [10, 0, 5, 73]))
game.add_child(Node.u8("claim", 0))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/entry_id")
return resp.child_value("game_2/entry_id")
def verify_game_entry_e(self, eid: int) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "entry_e")
game.set_attribute("ver", "0")
game.add_child(Node.u32("eid", eid))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2")
def verify_game_load_m(self, refid: str) -> List[Dict[str, int]]:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("method", "load_m")
game.set_attribute("ver", "0")
game.add_child(Node.string("dataid", refid))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2/new")
scores = []
for child in resp.child("game_2/new").children:
if child.name != "music":
continue
musicid = child.child_value("music_id")
chart = child.child_value("music_type")
clear_type = child.child_value("clear_type")
score = child.child_value("score")
grade = child.child_value("score_grade")
scores.append(
{
"id": musicid,
"chart": chart,
"clear_type": clear_type,
"score": score,
"grade": grade,
}
)
return scores
def verify_game_save_m(
self, location: str, refid: str, score: Dict[str, int]
) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("ver", "0")
game.set_attribute("method", "save_m")
game.add_child(Node.string("refid", refid))
game.add_child(Node.string("dataid", refid))
game.add_child(Node.u32("music_id", score["id"]))
game.add_child(Node.u32("music_type", score["chart"]))
game.add_child(Node.u32("score", score["score"]))
game.add_child(Node.u32("clear_type", score["clear_type"]))
game.add_child(Node.u32("score_grade", score["grade"]))
game.add_child(Node.u32("max_chain", 0))
game.add_child(Node.u32("critical", 0))
game.add_child(Node.u32("near", 0))
game.add_child(Node.u32("error", 0))
game.add_child(Node.u32("effective_rate", 100))
game.add_child(Node.u32("btn_rate", 0))
game.add_child(Node.u32("long_rate", 0))
game.add_child(Node.u32("vol_rate", 0))
game.add_child(Node.u8("mode", 0))
game.add_child(Node.u8("gauge_type", 0))
game.add_child(Node.u16("online_num", 0))
game.add_child(Node.u16("local_num", 0))
game.add_child(Node.string("locid", location))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2")
def verify_game_save_c(
self, location: str, refid: str, season: int, course: int
) -> None:
call = self.call_node()
game = Node.void("game_2")
call.add_child(game)
game.set_attribute("ver", "0")
game.set_attribute("method", "save_c")
game.add_child(Node.string("dataid", refid))
game.add_child(Node.s16("crsid", course))
game.add_child(Node.s16("ct", 2))
game.add_child(Node.s16("ar", 15000))
game.add_child(Node.s32("ssnid", season))
game.add_child(Node.string("locid", location))
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game_2")
def verify(self, cardid: Optional[str]) -> None:
# Verify boot sequence is okay
self.verify_services_get(
expected_services=[
"pcbtracker",
"pcbevent",
"local",
"message",
"facility",
"cardmng",
"package",
"posevent",
"pkglist",
"dlstatus",
"eacoin",
"lobby",
"ntp",
"keepalive",
]
)
paseli_enabled = self.verify_pcbtracker_alive()
self.verify_message_get()
self.verify_package_list()
location = self.verify_facility_get()
self.verify_pcbevent_put()
self.verify_eventlog_write(location)
self.verify_game_common()
self.verify_game_shop(location)
# Verify card registration and profile lookup
if cardid is not None:
card = cardid
else:
card = self.random_card()
print(f"Generated random card ID {card} for use.")
if cardid is None:
self.verify_cardmng_inquire(
card, msg_type="unregistered", paseli_enabled=paseli_enabled
)
ref_id = self.verify_cardmng_getrefid(card)
if len(ref_id) != 16:
raise Exception(
f"Invalid refid '{ref_id}' returned when registering card"
)
if ref_id != self.verify_cardmng_inquire(
card, msg_type="new", paseli_enabled=paseli_enabled
):
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
# SDVX doesn't read the new profile, it asks for the profile itself after calling new
self.verify_game_load(card, ref_id, msg_type="new")
self.verify_game_new(location, ref_id)
self.verify_game_load(card, ref_id, msg_type="existing")
else:
print("Skipping new card checks for existing card")
ref_id = self.verify_cardmng_inquire(
card, msg_type="query", paseli_enabled=paseli_enabled
)
# Verify pin handling and return card handling
self.verify_cardmng_authpass(ref_id, correct=True)
self.verify_cardmng_authpass(ref_id, correct=False)
if ref_id != self.verify_cardmng_inquire(
card, msg_type="query", paseli_enabled=paseli_enabled
):
raise Exception(f"Invalid refid '{ref_id}' returned when querying card")
# Verify account freezing
self.verify_game_frozen(ref_id, 900)
self.verify_game_frozen(ref_id, 0)
# Verify lobby functionality
self.verify_game_lounge()
eid = self.verify_game_entry_s()
self.verify_game_entry_e(eid)
if cardid is None:
# Verify profile loading and saving
profile = self.verify_game_load(card, ref_id, msg_type="existing")
if profile["name"] != self.NAME:
raise Exception(
f'Profile has incorrect name {profile["name"]} associated with it!'
)
if profile["packet"] != 0:
raise Exception("Profile has nonzero blocks associated with it!")
if profile["block"] != 0:
raise Exception("Profile has nonzero packets associated with it!")
if profile["blaster_energy"] != 0:
raise Exception(
"Profile has nonzero blaster energy associated with it!"
)
if profile["items"]:
raise Exception("Profile already has purchased items!")
if profile["appealcards"]:
raise Exception("Profile already has appeal cards!")
if profile["courses"]:
raise Exception("Profile already has finished courses!")
# Verify purchase failure, try buying song we can't afford
self.verify_game_buy(ref_id, 0, 29, 1, 10, 0, 29, 3, False)
self.verify_game_save(
location,
ref_id,
packet=123,
block=234,
blaster_energy=42,
appealcards=[],
)
profile = self.verify_game_load(card, ref_id, msg_type="existing")
if profile["name"] != self.NAME:
raise Exception(
f'Profile has incorrect name {profile["name"]} associated with it!'
)
if profile["packet"] != 123:
raise Exception("Profile has invalid blocks associated with it!")
if profile["block"] != 234:
raise Exception("Profile has invalid packets associated with it!")
if profile["blaster_energy"] != 42:
raise Exception(
"Profile has invalid blaster energy associated with it!"
)
if profile["items"]:
raise Exception("Profile already has purchased items!")
if profile["appealcards"]:
raise Exception("Profile already has appeal cards!")
if profile["courses"]:
raise Exception("Profile already has finished courses!")
self.verify_game_save(
location, ref_id, packet=1, block=2, blaster_energy=3, appealcards=[]
)
profile = self.verify_game_load(card, ref_id, msg_type="existing")
if profile["name"] != self.NAME:
raise Exception(
f'Profile has incorrect name {profile["name"]} associated with it!'
)
if profile["packet"] != 124:
raise Exception("Profile has invalid blocks associated with it!")
if profile["block"] != 236:
raise Exception("Profile has invalid packets associated with it!")
if profile["blaster_energy"] != 45:
raise Exception(
"Profile has invalid blaster energy associated with it!"
)
if profile["items"]:
raise Exception("Profile has invalid purchased items!")
if profile["appealcards"]:
raise Exception("Profile has invalid appeal cards!")
if profile["courses"]:
raise Exception("Profile has invalid finished courses!")
# Verify purchase success, buy a song we can afford now
self.verify_game_buy(ref_id, 0, 29, 1, 10, 0, 29, 3, True)
profile = self.verify_game_load(card, ref_id, msg_type="existing")
if profile["name"] != self.NAME:
raise Exception(
f'Profile has incorrect name {profile["name"]} associated with it!'
)
if profile["packet"] != 124:
raise Exception("Profile has invalid blocks associated with it!")
if profile["block"] != 226:
raise Exception("Profile has invalid packets associated with it!")
if profile["blaster_energy"] != 45:
raise Exception(
"Profile has invalid blaster energy associated with it!"
)
if 0 not in profile["items"] or 29 not in profile["items"][0]:
raise Exception("Purchase didn't add to profile!")
if profile["items"][0][29] != 3:
raise Exception("Purchase parameters are wrong!")
if profile["appealcards"]:
raise Exception("Profile has invalid appeal cards!")
if profile["courses"]:
raise Exception("Profile has invalid finished courses!")
# Verify that we can earn appeal cards
self.verify_game_save(
location,
ref_id,
packet=0,
block=0,
blaster_energy=0,
appealcards=[1001, 1002, 1003, 1004, 1005],
)
profile = self.verify_game_load(card, ref_id, msg_type="existing")
for i in [1001, 1002, 1003, 1004, 1005]:
if i not in profile["appealcards"]:
raise Exception(f"Profile missing appeal card {i}")
if profile["appealcards"][i] != 0:
raise Exception(f"Profile has bad count for appeal card {i}")
# Verify that we can finish skill analyzer courses
self.verify_game_save_c(location, ref_id, 14, 3)
profile = self.verify_game_load(card, ref_id, msg_type="existing")
if 14 not in profile["courses"] or 3 not in profile["courses"][14]:
raise Exception("Course didn't add to profile!")
if profile["courses"][14][3]["achievement_rate"] != 15000:
raise Exception("Course didn't save achievement rate!")
if profile["courses"][14][3]["clear_type"] != 2:
raise Exception("Course didn't save clear type!")
# Verify empty profile has no scores on it
scores = self.verify_game_load_m(ref_id)
if len(scores) > 0:
raise Exception("Score on an empty profile!")
# Verify score saving and updating
for phase in [1, 2]:
if phase == 1:
dummyscores = [
# An okay score on a chart
{
"id": 1,
"chart": 1,
"grade": 3,
"clear_type": 2,
"score": 765432,
},
# A good score on an easier chart of the same song
{
"id": 1,
"chart": 0,
"grade": 6,
"clear_type": 3,
"score": 7654321,
},
# A bad score on a hard chart
{
"id": 2,
"chart": 2,
"grade": 1,
"clear_type": 1,
"score": 12345,
},
# A terrible score on an easy chart
{
"id": 3,
"chart": 0,
"grade": 1,
"clear_type": 1,
"score": 123,
},
]
if phase == 2:
dummyscores = [
# A better score on the same chart
{
"id": 1,
"chart": 1,
"grade": 5,
"clear_type": 3,
"score": 8765432,
},
# A worse score on another same chart
{
"id": 1,
"chart": 0,
"grade": 4,
"clear_type": 2,
"score": 6543210,
"expected_score": 7654321,
"expected_clear_type": 3,
"expected_grade": 6,
},
]
for dummyscore in dummyscores:
self.verify_game_save_m(location, ref_id, dummyscore)
scores = self.verify_game_load_m(ref_id)
for expected in dummyscores:
actual = None
for received in scores:
if (
received["id"] == expected["id"]
and received["chart"] == expected["chart"]
):
actual = received
break
if actual is None:
raise Exception(
f"Didn't find song {expected['id']} chart {expected['chart']} in response!"
)
if "expected_score" in expected:
expected_score = expected["expected_score"]
else:
expected_score = expected["score"]
if "expected_grade" in expected:
expected_grade = expected["expected_grade"]
else:
expected_grade = expected["grade"]
if "expected_clear_type" in expected:
expected_clear_type = expected["expected_clear_type"]
else:
expected_clear_type = expected["clear_type"]
if actual["score"] != expected_score:
raise Exception(
f'Expected a score of \'{expected_score}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got score \'{actual["score"]}\''
)
if actual["grade"] != expected_grade:
raise Exception(
f'Expected a grade of \'{expected_grade}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got grade \'{actual["grade"]}\''
)
if actual["clear_type"] != expected_clear_type:
raise Exception(
f'Expected a clear_type of \'{expected_clear_type}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got clear_type \'{actual["clear_type"]}\''
)
# Sleep so we don't end up putting in score history on the same second
time.sleep(1)
else:
print("Skipping score checks for existing card")
# Verify high score tables
self.verify_game_hiscore(location)
# Verify paseli handling
if paseli_enabled:
print("PASELI enabled for this PCBID, executing PASELI checks")
else:
print("PASELI disabled for this PCBID, skipping PASELI checks")
return
sessid, balance = self.verify_eacoin_checkin(card)
if balance == 0:
print("Skipping PASELI consume check because card has 0 balance")
else:
self.verify_eacoin_consume(sessid, balance, random.randint(0, balance))
self.verify_eacoin_checkout(sessid)