import random import time from typing import Any, Dict, List, Optional from bemani.client.base import BaseClient from bemani.protocol import Node class SoundVoltexHeavenlyHavenClient(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", "2.3.8")) 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_exception(self, location: str) -> None: call = self.call_node() game = Node.void("game") game.set_attribute("method", "sv4_exception") game.add_child(Node.string("text", "")) game.add_child(Node.string("lid", location)) call.add_child(game) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game") def verify_game_hiscore(self, location: str) -> None: call = self.call_node() game = Node.void("game") game.set_attribute("ver", "0") game.set_attribute("method", "sv4_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/sc/d/id") self.assert_path(resp, "response/game/sc/d/ty") self.assert_path(resp, "response/game/sc/d/a_sq") self.assert_path(resp, "response/game/sc/d/a_nm") self.assert_path(resp, "response/game/sc/d/a_sc") self.assert_path(resp, "response/game/sc/d/cr") self.assert_path(resp, "response/game/sc/d/avg_sc") def verify_game_shop(self, location: str) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_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-B01")) game.add_child(Node.string("etc", "TaxMode:1,BasicRate:100/1,FirstFree:0")) setting = Node.void("setting") game.add_child(setting) setting.add_child(Node.s32("coin_slot", 0)) setting.add_child(Node.s32("game_start", 1)) setting.add_child(Node.string("schedule", "0,0,0,0,0,0,0")) setting.add_child(Node.string("reference", "1,1,1")) setting.add_child(Node.string("basic_rate", "100,100,100")) setting.add_child(Node.s32("tax_rate", 1)) setting.add_child(Node.string("time_service", "0,0,0")) setting.add_child(Node.string("service_value", "10,10,10")) setting.add_child(Node.string("service_limit", "10,10,10")) setting.add_child( Node.string("service_time", "07:00-11:00,07:00-11:00,07:00-11:00") ) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/nxt_time") def verify_game_new(self, location: str, refid: str) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_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") def verify_game_frozen(self, refid: str, time: int) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_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/result") def verify_game_load( self, cardid: str, refid: str, msg_type: str ) -> Dict[str, Any]: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_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/result") if resp.child_value("game/result") != 1: raise Exception("Invalid result for new profile!") return None if msg_type == "existing": self.assert_path(resp, "response/game/name") self.assert_path(resp, "response/game/code") self.assert_path(resp, "response/game/sdvx_id") self.assert_path(resp, "response/game/gamecoin_packet") self.assert_path(resp, "response/game/gamecoin_block") self.assert_path(resp, "response/game/skill_name_id") self.assert_path(resp, "response/game/skill_base_id") self.assert_path(resp, "response/game/skill_level") self.assert_path(resp, "response/game/blaster_energy") self.assert_path(resp, "response/game/blaster_count") self.assert_path(resp, "response/game/play_count") self.assert_path(resp, "response/game/today_count") self.assert_path(resp, "response/game/play_chain") self.assert_path(resp, "response/game/item") self.assert_path(resp, "response/game/skill") self.assert_path(resp, "response/game/param") self.assert_path(resp, "response/game/pbc_infection/packet/before") self.assert_path(resp, "response/game/pbc_infection/packet/after") self.assert_path(resp, "response/game/pbc_infection/block/before") self.assert_path(resp, "response/game/pbc_infection/block/after") self.assert_path(resp, "response/game/pbc_infection/coloris/before") self.assert_path(resp, "response/game/pbc_infection/coloris/after") self.assert_path(resp, "response/game/pb_infection/packet/before") self.assert_path(resp, "response/game/pb_infection/packet/after") self.assert_path(resp, "response/game/pb_infection/block/before") self.assert_path(resp, "response/game/pb_infection/block/after") items: Dict[int, Dict[int, int]] = {} for child in resp.child("game/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 courses: Dict[int, Dict[int, Dict[str, int]]] = {} for child in resp.child("game/skill").children: if child.name != "course": continue crsid = child.child_value("crsid") season = child.child_value("ssnid") achievement_rate = child.child_value("ar") clear_type = child.child_value("ct") grade = child.child_value("gr") score = child.child_value("sc") if season not in courses: courses[season] = {} courses[season][crsid] = { "achievement_rate": achievement_rate, "clear_type": clear_type, "grade": grade, "score": score, } return { "name": resp.child_value("game/name"), "packet": resp.child_value("game/gamecoin_packet"), "block": resp.child_value("game/gamecoin_block"), "blaster_energy": resp.child_value("game/blaster_energy"), "skill_level": resp.child_value("game/skill_level"), "items": items, "courses": courses, } else: raise Exception(f"Invalid game load type {msg_type}") def verify_game_save( self, location: str, refid: str, packet: int, block: int, blaster_energy: int ) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_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.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.u8("ars_option", 0)) game.add_child(Node.u8("notes_option", 0)) game.add_child(Node.u8("early_late_disp", 0)) game.add_child(Node.s32("draw_adjust", 0)) game.add_child(Node.u8("eff_c_left", 0)) game.add_child(Node.u8("eff_c_right", 1)) game.add_child(Node.u32("earned_gamecoin_packet", packet)) game.add_child(Node.u32("earned_gamecoin_block", block)) item = Node.void("item") game.add_child(item) game.add_child(Node.s16("skill_name_id", 0)) game.add_child(Node.s16("skill_base_id", 0)) game.add_child(Node.s16("skill_name", 0)) game.add_child(Node.s32("earned_blaster_energy", blaster_energy)) game.add_child(Node.u32("blaster_count", 0)) printn = Node.void("print") game.add_child(printn) printn.add_child(Node.s32("count", 0)) ea_shop = Node.void("ea_shop") game.add_child(ea_shop) ea_shop.add_child(Node.s32("used_packet_booster", 0)) ea_shop.add_child(Node.s32("used_block_booster", 0)) game.add_child(Node.s8("start_option", 1)) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game") def verify_game_common(self, loc: str) -> None: call = self.call_node() game = Node.void("game") game.set_attribute("ver", "0") game.set_attribute("method", "sv4_common") game.add_child(Node.string("locid", loc)) game.add_child(Node.string("cstcode", "")) game.add_child(Node.string("cpycode", "")) game.add_child(Node.string("hadid", "00010203040506070809")) game.add_child(Node.string("licid", "00010203040506070809")) game.add_child(Node.string("actid", self.pcbid)) call.add_child(game) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/music_limited") self.assert_path(resp, "response/game/catalog") self.assert_path(resp, "response/game/event/info/event_id") self.assert_path(resp, "response/game/reitaisai2018") self.assert_path(resp, "response/game/volte_factory/goods") self.assert_path(resp, "response/game/volte_factory/stock") self.assert_path(resp, "response/game/appealcard") self.assert_path(resp, "response/game/extend") self.assert_path(resp, "response/game/skill_course/info/season_id") self.assert_path(resp, "response/game/skill_course/info/season_name") self.assert_path(resp, "response/game/skill_course/info/season_new_flg") self.assert_path(resp, "response/game/skill_course/info/course_id") self.assert_path(resp, "response/game/skill_course/info/course_name") self.assert_path(resp, "response/game/skill_course/info/course_type") self.assert_path(resp, "response/game/skill_course/info/skill_level") self.assert_path(resp, "response/game/skill_course/info/skill_name_id") self.assert_path(resp, "response/game/skill_course/info/matching_assist") self.assert_path(resp, "response/game/skill_course/info/clear_rate") self.assert_path(resp, "response/game/skill_course/info/avg_score") self.assert_path(resp, "response/game/skill_course/info/track/track_no") self.assert_path(resp, "response/game/skill_course/info/track/music_id") self.assert_path(resp, "response/game/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") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_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)) item = Node.void("item") game.add_child(item) item.add_child(Node.u32("item_type", itemtype)) item.add_child(Node.u32("item_id", itemid)) item.add_child(Node.u32("param", param)) item.add_child(Node.u32("price", price)) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/gamecoin_packet") self.assert_path(resp, "response/game/gamecoin_block") self.assert_path(resp, "response/game/result") if success: if resp.child_value("game/result") != 0: raise Exception("Failed to purchase!") else: if resp.child_value("game/result") == 0: raise Exception("Purchased when shouldn't have!") def verify_game_lounge(self) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_lounge") game.set_attribute("ver", "0") # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/interval") def verify_game_entry_s(self) -> int: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_entry_s") game.add_child(Node.u8("c_ver", 174)) 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", 492)) 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/entry_id") return resp.child_value("game/entry_id") def verify_game_entry_e(self, eid: int) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_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") def verify_game_save_e(self, location: str, cardid: str, refid: str) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_save_e") game.set_attribute("ver", "0") game.add_child(Node.string("locid", location)) game.add_child(Node.string("cardnumber", cardid)) game.add_child(Node.string("refid", refid)) game.add_child(Node.s32("playid", 1)) game.add_child(Node.bool("is_paseli", False)) game.add_child(Node.s32("online_num", 0)) game.add_child(Node.s32("local_num", 0)) game.add_child(Node.s32("start_option", 0)) game.add_child(Node.s32("print_num", 0)) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game") self.assert_path(resp, "response/game/pbc_infection/packet/before") self.assert_path(resp, "response/game/pbc_infection/packet/after") self.assert_path(resp, "response/game/pbc_infection/block/before") self.assert_path(resp, "response/game/pbc_infection/block/after") self.assert_path(resp, "response/game/pbc_infection/coloris/before") self.assert_path(resp, "response/game/pbc_infection/coloris/after") self.assert_path(resp, "response/game/pb_infection/packet/before") self.assert_path(resp, "response/game/pb_infection/packet/after") self.assert_path(resp, "response/game/pb_infection/block/before") self.assert_path(resp, "response/game/pb_infection/block/after") def verify_game_play_s(self) -> int: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_play_s") game.set_attribute("ver", "0") # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/play_id") return resp.child_value("game/play_id") def verify_game_play_e(self, location: str, refid: str, play_id: int) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_play_e") game.add_child(Node.string("refid", refid)) game.add_child(Node.u32("play_id", play_id)) game.add_child(Node.s8("start_type", 1)) game.add_child(Node.s8("mode", 2)) game.add_child(Node.s16("track_num", 3)) game.add_child(Node.s32("s_coin", 0)) game.add_child(Node.s32("s_paseli", 247)) game.add_child(Node.u32("print_card", 0)) game.add_child(Node.u32("print_result", 0)) game.add_child(Node.u32("blaster_num", 0)) game.add_child(Node.u32("today_cnt", 1)) game.add_child(Node.u32("play_chain", 1)) game.add_child(Node.u32("week_play_cnt", 0)) game.add_child(Node.u32("week_chain", 0)) game.add_child(Node.string("locid", location)) game.add_child(Node.u16("drop_frame", 16169)) game.add_child(Node.u16("drop_frame_max", 11984)) game.add_child(Node.u16("drop_count", 6)) game.add_child(Node.string("etc", "play_t:605")) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game") def verify_game_load_m(self, refid: str) -> List[Dict[str, int]]: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_load_m") game.set_attribute("ver", "0") game.add_child(Node.string("refid", refid)) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game/music") scores = [] for child in resp.child("game/music").children: if child.name != "info": continue musicid = child.child_value("param")[0] chart = child.child_value("param")[1] clear_type = child.child_value("param")[3] score = child.child_value("param")[2] grade = child.child_value("param")[4] 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, play_id: int, score: Dict[str, int] ) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_save_m") game.add_child(Node.string("refid", refid)) game.add_child(Node.string("dataid", refid)) game.add_child(Node.u32("play_id", play_id)) game.add_child(Node.u16("track_no", 0)) 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.u8("notes_option", 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") def verify_game_load_r(self, refid: str) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("method", "sv4_load_r") game.set_attribute("ver", "0") game.add_child(Node.string("refid", refid)) # Swap with server resp = self.exchange("", call) # Verify that response is correct self.assert_path(resp, "response/game") def verify_game_save_c( self, location: str, refid: str, play_id: int, season: int, course: int ) -> None: call = self.call_node() game = Node.void("game") call.add_child(game) game.set_attribute("ver", "0") game.set_attribute("method", "sv4_save_c") game.add_child(Node.string("refid", refid)) game.add_child(Node.u32("play_id", play_id)) game.add_child(Node.s32("ssnid", season)) 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.u32("sc", 1234567)) game.add_child(Node.s16("gr", 7)) 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") def verify(self, cardid: Optional[str]) -> None: # Verify boot sequence is okay self.verify_services_get( expected_services=[ "pcbtracker", "pcbevent", "local", "local2", "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(location) self.verify_game_shop(location) self.verify_game_exception(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 rivals node (necessary to return but can hold nothing) self.verify_game_load_r(ref_id) # Verify account freezing self.verify_game_frozen(ref_id, 900) play_id = self.verify_game_play_s() self.verify_game_save_e(location, card, ref_id) # 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["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 ) 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["courses"]: raise Exception("Profile already has finished courses!") self.verify_game_save(location, ref_id, packet=1, block=2, blaster_energy=3) 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["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["courses"]: raise Exception("Profile has invalid finished courses!") # Verify that we can finish skill analyzer courses self.verify_game_save_c(location, ref_id, play_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!") if profile["courses"][14][3]["score"] != 1234567: raise Exception("Course didn't save score!") if profile["courses"][14][3]["grade"] != 7: raise Exception("Course didn't save grade!") # 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, play_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") # Unfreeze account self.verify_game_play_e(location, ref_id, play_id) self.verify_game_frozen(ref_id, 0) # 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)