1
0
mirror of synced 2024-12-03 01:57:18 +01:00
bemaniutils/bemani/client/jubeat/prop.py

787 lines
33 KiB
Python

import random
import time
from typing import Optional, Dict, List, Tuple, Any
from bemani.client.base import BaseClient
from bemani.common import CardCipher
from bemani.protocol import Node
class JubeatPropClient(BaseClient):
NAME = 'TEST'
def verify_shopinfo_regist(self) -> None:
call = self.call_node()
# Construct node
shopinfo = Node.void('shopinfo')
shopinfo.set_attribute('method', 'regist')
call.add_child(shopinfo)
shop = Node.void('shop')
shopinfo.add_child(shop)
shop.add_child(Node.string('name', ''))
shop.add_child(Node.string('pref', 'JP-14'))
shop.add_child(Node.string('softwareid', ''))
shop.add_child(Node.string('systemid', self.pcbid))
shop.add_child(Node.string('hardwareid', '01020304050607080900'))
shop.add_child(Node.string('locationid', 'US-1'))
shop.add_child(Node.string('monitor', 'D26L155 6252 151'))
testmode = Node.void('testmode')
shop.add_child(testmode)
# Swap with server
resp = self.exchange('', call)
# Verify that response is correct
self.assert_path(resp, "response/shopinfo/data/cabid")
self.assert_path(resp, "response/shopinfo/data/locationid")
self.assert_path(resp, "response/shopinfo/data/tax_phase")
self.assert_path(resp, "response/shopinfo/data/facility/exist")
self.assert_path(resp, "response/shopinfo/data/info/event_info")
self.assert_path(resp, "response/shopinfo/data/info/share_music")
self.assert_path(resp, "response/shopinfo/data/info/bonus_music")
self.assert_path(resp, "response/shopinfo/data/info/only_now_music")
self.assert_path(resp, "response/shopinfo/data/info/fc_challenge/today/music_id")
self.assert_path(resp, "response/shopinfo/data/info/white_music_list")
self.assert_path(resp, "response/shopinfo/data/info/open_music_list")
self.assert_path(resp, "response/shopinfo/data/info/cabinet_survey/id")
self.assert_path(resp, "response/shopinfo/data/info/cabinet_survey/status")
self.assert_path(resp, "response/shopinfo/data/info/kaitou_bisco/remaining_days")
self.assert_path(resp, "response/shopinfo/data/info/league/status")
self.assert_path(resp, "response/shopinfo/data/info/bistro/bistro_id")
self.assert_path(resp, "response/shopinfo/data/info/jbox/point")
self.assert_path(resp, "response/shopinfo/data/info/jbox/emblem/normal/index")
self.assert_path(resp, "response/shopinfo/data/info/jbox/emblem/premium/index")
def verify_demodata_get_news(self) -> None:
call = self.call_node()
# Construct node
demodata = Node.void('demodata')
call.add_child(demodata)
demodata.set_attribute('method', 'get_news')
# Swap with server
resp = self.exchange('', call)
# Verify that response is correct
self.assert_path(resp, "response/demodata/data/officialnews")
def __verify_profile(self, resp: Node) -> int:
self.assert_path(resp, "response/gametop/data/info/event_info")
self.assert_path(resp, "response/gametop/data/info/share_music")
self.assert_path(resp, "response/gametop/data/info/bonus_music")
self.assert_path(resp, "response/gametop/data/info/only_now_music")
self.assert_path(resp, "response/gametop/data/info/fc_challenge/today/music_id")
self.assert_path(resp, "response/gametop/data/info/white_music_list")
self.assert_path(resp, "response/gametop/data/info/open_music_list")
self.assert_path(resp, "response/gametop/data/info/cabinet_survey/id")
self.assert_path(resp, "response/gametop/data/info/cabinet_survey/status")
self.assert_path(resp, "response/gametop/data/info/kaitou_bisco/remaining_days")
self.assert_path(resp, "response/gametop/data/info/league/status")
self.assert_path(resp, "response/gametop/data/info/bistro/bistro_id")
self.assert_path(resp, "response/gametop/data/info/jbox/point")
self.assert_path(resp, "response/gametop/data/info/jbox/emblem/normal/index")
self.assert_path(resp, "response/gametop/data/info/jbox/emblem/premium/index")
for item in [
'jubility',
'jubility_yday',
'tune_cnt',
'save_cnt',
'saved_cnt',
'fc_cnt',
'ex_cnt',
'clear_cnt',
'pf_cnt',
'match_cnt',
'beat_cnt',
'mynews_cnt',
'bonus_tune_points',
'is_bonus_tune_played',
'inherit',
'mtg_entry_cnt',
'mtg_hold_cnt',
'mtg_result',
]:
self.assert_path(resp, f"response/gametop/data/player/info/{item}")
for item in [
'music_list',
'secret_list',
'theme_list',
'marker_list',
'title_list',
'parts_list',
'emblem_list',
'new/secret_list',
'new/theme_list',
'new/marker_list',
]:
self.assert_path(resp, f"response/gametop/data/player/item/{item}")
for item in [
'play_time',
'shopname',
'areaname',
'expert_option',
'category',
'sort',
'music_id',
'seq_id',
]:
self.assert_path(resp, f"response/gametop/data/player/last/{item}")
for item in [
'marker',
'theme',
'title',
'parts',
'rank_sort',
'combo_disp',
'emblem',
'matching',
'hazard',
'hard',
]:
self.assert_path(resp, f"response/gametop/data/player/last/settings/{item}")
# Misc stuff
self.assert_path(resp, "response/gametop/data/player/session_id")
self.assert_path(resp, "response/gametop/data/player/event_flag")
# Profile settings
self.assert_path(resp, "response/gametop/data/player/name")
self.assert_path(resp, "response/gametop/data/player/jid")
# Required nodes for events and stuff
self.assert_path(resp, "response/gametop/data/player/history")
self.assert_path(resp, "response/gametop/data/player/lab_edit_seq")
self.assert_path(resp, "response/gametop/data/player/event_info")
self.assert_path(resp, "response/gametop/data/player/cabinet_survey/read_flag")
self.assert_path(resp, "response/gametop/data/player/kaitou_bisco/read_flag")
self.assert_path(resp, "response/gametop/data/player/navi/flag")
self.assert_path(resp, "response/gametop/data/player/fc_challenge/today/music_id")
self.assert_path(resp, "response/gametop/data/player/fc_challenge/today/state")
self.assert_path(resp, "response/gametop/data/player/fc_challenge/whim/music_id")
self.assert_path(resp, "response/gametop/data/player/fc_challenge/whim/state")
self.assert_path(resp, "response/gametop/data/player/news/checked")
self.assert_path(resp, "response/gametop/data/player/news/checked_flag")
self.assert_path(resp, "response/gametop/data/player/rivallist")
self.assert_path(resp, "response/gametop/data/player/free_first_play/is_available")
self.assert_path(resp, "response/gametop/data/player/free_first_play/point")
self.assert_path(resp, "response/gametop/data/player/free_first_play/point_used")
self.assert_path(resp, "response/gametop/data/player/free_first_play/come_come_jbox/is_valid")
self.assert_path(resp, "response/gametop/data/player/free_first_play/come_come_jbox/end_time_if_paired")
self.assert_path(resp, "response/gametop/data/player/jbox/point")
self.assert_path(resp, "response/gametop/data/player/jbox/emblem/normal/index")
self.assert_path(resp, "response/gametop/data/player/jbox/emblem/premium/index")
self.assert_path(resp, "response/gametop/data/player/career/level")
self.assert_path(resp, "response/gametop/data/player/career/point")
self.assert_path(resp, "response/gametop/data/player/career/param")
self.assert_path(resp, "response/gametop/data/player/career/is_unlocked")
self.assert_path(resp, "response/gametop/data/player/league/is_first_play")
self.assert_path(resp, "response/gametop/data/player/league/class")
self.assert_path(resp, "response/gametop/data/player/league/subclass")
self.assert_path(resp, "response/gametop/data/player/new_music")
self.assert_path(resp, "response/gametop/data/player/eapass_privilege/emblem_list")
self.assert_path(resp, "response/gametop/data/player/bonus_music/music")
self.assert_path(resp, "response/gametop/data/player/bonus_music/event_id")
self.assert_path(resp, "response/gametop/data/player/bonus_music/till_time")
self.assert_path(resp, "response/gametop/data/player/bistro/chef/id")
self.assert_path(resp, "response/gametop/data/player/bistro/carry_over")
self.assert_path(resp, "response/gametop/data/player/bistro/route_list/route_count")
self.assert_path(resp, "response/gametop/data/player/bistro/extension")
self.assert_path(resp, "response/gametop/data/player/gift_list")
# Return the jid
return resp.child_value('gametop/data/player/jid')
def verify_gameend_regist(
self,
ref_id: str,
jid: int,
scores: List[Dict[str, Any]],
course: Dict[str, Any],
league: Optional[Tuple[int, Tuple[int, int, int]]],
) -> None:
call = self.call_node()
# Construct node
gameend = Node.void('gameend')
call.add_child(gameend)
gameend.set_attribute('method', 'regist')
gameend.add_child(Node.s32('retry', 0))
data = Node.void('data')
gameend.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.string('refid', ref_id))
player.add_child(Node.s32('jid', jid))
player.add_child(Node.string('name', self.NAME))
result = Node.void('result')
data.add_child(result)
result.set_attribute('count', str(len(scores)))
# Send scores
scoreid = 0
for score in scores:
# Always played
bits = 0x1
if score['clear']:
bits |= 0x2
if score['fc']:
bits |= 0x4
if score['ex']:
bits |= 0x8
# Intentionally starting at 1 because that's what the game does
scoreid = scoreid + 1
tune = Node.void('tune')
result.add_child(tune)
tune.set_attribute('id', str(scoreid))
tune.set_attribute('count', '0')
tune.add_child(Node.s32('music', score['id']))
player_1 = Node.void('player')
tune.add_child(player_1)
player_1.set_attribute('rank', '1')
scorenode = Node.s32('score', score['score'])
player_1.add_child(scorenode)
scorenode.set_attribute('seq', str(score['chart']))
scorenode.set_attribute('clear', str(bits))
scorenode.set_attribute('combo', '69')
player_1.add_child(Node.u8_array('mbar', [239, 175, 170, 170, 190, 234, 187, 158, 153, 230, 170, 90, 102, 170, 85, 150, 150, 102, 85, 234, 171, 169, 157, 150, 170, 101, 230, 90, 214, 255]))
if len(course) > 0:
coursenode = Node.void('course')
player.add_child(coursenode)
coursenode.add_child(Node.s32('course_id', course['course_id']))
coursenode.add_child(Node.u8('rating', course['rating']))
index = 0
for coursescore in course['scores']:
music = Node.void('music')
coursenode.add_child(music)
music.set_attribute('index', str(index))
music.add_child(Node.s32('score', coursescore))
index = index + 1
if league is not None:
leaguenode = Node.void('league')
player.add_child(leaguenode)
leaguenode.add_child(Node.s32('league_id', league[0]))
leaguenode.add_child(Node.bool('is_first_play', False))
leaguenode.add_child(Node.bool('is_checked', True))
index = 0
for leaguescore in league[1]:
musicnode = Node.void('music')
leaguenode.add_child(musicnode)
musicnode.set_attribute('index', str(index))
index = index + 1
scorenode = Node.s32('score', leaguescore)
musicnode.add_child(scorenode)
scorenode.set_attribute('clear', '3')
# Swap with server
resp = self.exchange('', call)
self.assert_path(resp, "response/gameend/data/player/session_id")
def verify_gametop_regist(self, card_id: str, ref_id: str) -> int:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'regist')
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.string('refid', ref_id))
player.add_child(Node.string('datid', ref_id))
player.add_child(Node.string('uid', card_id))
player.add_child(Node.bool('inherit', True))
player.add_child(Node.string('name', self.NAME))
# Swap with server
resp = self.exchange('', call)
# Verify nodes that cause crashes if they don't exist
return self.__verify_profile(resp)
def verify_gametop_get_pdata(self, card_id: str, ref_id: str) -> int:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'get_pdata')
retry = Node.s32('retry', 0)
gametop.add_child(retry)
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.string('refid', ref_id))
player.add_child(Node.string('datid', ref_id))
player.add_child(Node.string('uid', card_id))
player.add_child(Node.string('card_no', CardCipher.encode(card_id)))
# Swap with server
resp = self.exchange('', call)
# Verify nodes that cause crashes if they don't exist
return self.__verify_profile(resp)
def verify_gametop_get_mdata(self, jid: int) -> Dict[str, List[Dict[str, Any]]]:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'get_mdata')
retry = Node.s32('retry', 0)
gametop.add_child(retry)
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.s32('jid', jid))
# Technically the game sends this same packet 3 times, one with
# each value 1, 2, 3 here. Unclear why, but we won't emulate it.
player.add_child(Node.s8('mdata_ver', 1))
player.add_child(Node.bool('rival', False))
# Swap with server
resp = self.exchange('', call)
# Parse out scores
self.assert_path(resp, "response/gametop/data/player/mdata_list")
ret = {}
for musicdata in resp.child('gametop/data/player/mdata_list').children:
if musicdata.name != 'musicdata':
raise Exception('Unexpected node in playdata!')
music_id = musicdata.attribute('music_id')
scores_by_chart: List[Dict[str, int]] = [{}, {}, {}]
def extract_cnts(name: str, val: List[int]) -> None:
scores_by_chart[0][name] = val[0]
scores_by_chart[1][name] = val[1]
scores_by_chart[2][name] = val[2]
extract_cnts('plays', musicdata.child_value('play_cnt'))
extract_cnts('clears', musicdata.child_value('clear_cnt'))
extract_cnts('full_combos', musicdata.child_value('fc_cnt'))
extract_cnts('excellents', musicdata.child_value('ex_cnt'))
extract_cnts('score', musicdata.child_value('score'))
extract_cnts('medal', musicdata.child_value('clear'))
ret[music_id] = scores_by_chart
return ret
def verify_gametop_get_course(self, jid: int) -> List[Dict[str, Any]]:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'get_course')
gametop.add_child(Node.s32('retry', 0))
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.s32('jid', jid))
# Swap with server
resp = self.exchange('', call)
# Verify expected nodes
self.assert_path(resp, "response/gametop/data/course_list")
self.assert_path(resp, "response/gametop/data/player_list")
self.assert_path(resp, "response/gametop/data/last_course_id")
playernode = None
for player in resp.child('gametop/data/player_list').children:
if player.child_value('jid') == jid:
playernode = player
break
if playernode is None:
raise Exception(f"Didn't find any scores for ExtID {jid}")
ret = []
for result in playernode.child('result_list').children:
if result.name != 'result':
raise Exception('Unexpected node in result_list!')
course_id = result.child_value('id')
rating = result.child_value('rating')
scores = result.child_value('score')
ret.append({'course_id': course_id, 'rating': rating, 'scores': scores})
return ret
def verify_gametop_get_league(self, jid: int) -> Tuple[int, Tuple[int, int, int]]:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'get_league')
gametop.add_child(Node.s32('retry', 0))
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.s32('jid', jid))
player.add_child(Node.s32_array('rival_jid', [0] * 3))
# Swap with server
resp = self.exchange('', call)
# Verify expected nodes
self.assert_path(resp, "response/gametop/data/league_list/league/player_list")
self.assert_path(resp, "response/gametop/data/last_class")
self.assert_path(resp, "response/gametop/data/last_subclass")
self.assert_path(resp, "response/gametop/data/is_checked")
leagueid = resp.child_value('gametop/data/league_list/league/id')
playernode = None
for player in resp.child('gametop/data/league_list/league/player_list').children:
if player.child_value('jid') == jid:
playernode = player
break
if playernode is None:
raise Exception(f"Didn't find any scores for ExtID {jid}")
result = playernode.child_value('result/score')
if result is not None:
return (leagueid, (result[0], result[1], result[2]))
else:
return (leagueid, (0, 0, 0))
def verify_gametop_get_meeting(self, jid: int) -> None:
call = self.call_node()
# Construct node
gametop = Node.void('gametop')
call.add_child(gametop)
gametop.set_attribute('method', 'get_meeting')
gametop.add_child(Node.s32('retry', 0))
data = Node.void('data')
gametop.add_child(data)
player = Node.void('player')
data.add_child(player)
player.add_child(Node.s32('jid', jid))
# Swap with server
resp = self.exchange('', call)
# Verify expected nodes
self.assert_path(resp, "response/gametop/data/meeting/single")
self.assert_path(resp, "response/gametop/data/meeting/tag")
self.assert_path(resp, "response/gametop/data/reward/total")
self.assert_path(resp, "response/gametop/data/reward/point")
def verify_demodata_get_hitchart(self) -> None:
call = self.call_node()
# Construct node
gametop = Node.void('demodata')
call.add_child(gametop)
gametop.set_attribute('method', 'get_hitchart')
# Swap with server
resp = self.exchange('', call)
# Verify expected nodes
self.assert_path(resp, "response/demodata/data/update")
self.assert_path(resp, "response/demodata/data/hitchart_lic")
self.assert_path(resp, "response/demodata/data/hitchart_org")
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()
self.verify_facility_get()
self.verify_pcbevent_put()
self.verify_shopinfo_regist()
self.verify_demodata_get_news()
self.verify_demodata_get_hitchart()
# 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')
self.verify_gametop_regist(card, ref_id)
else:
print("Skipping new card checks for existing card")
ref_id = self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled)
# Verify pin handling and return card handling
self.verify_cardmng_authpass(ref_id, correct=True)
self.verify_cardmng_authpass(ref_id, correct=False)
if ref_id != self.verify_cardmng_inquire(card, msg_type='query', paseli_enabled=paseli_enabled):
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
if cardid is None:
# Verify score handling
jid = self.verify_gametop_get_pdata(card, ref_id)
scores = self.verify_gametop_get_mdata(jid)
courses = self.verify_gametop_get_course(jid)
league = self.verify_gametop_get_league(jid)
self.verify_gametop_get_meeting(jid)
if scores is None:
raise Exception('Expected to get scores back, didn\'t get anything!')
if courses is None:
raise Exception('Expected to get courses back, didn\'t get anything!')
if league is None:
raise Exception('Expected to get league back, didn\'t get anything!')
if len(scores) > 0:
raise Exception('Got nonzero score count on a new card!')
if len(courses) > 0:
raise Exception('Got nonzero course count on a new card!')
if league[1][0] != 0 or league[1][1] != 0 or league[1][2] != 0:
raise Exception('Got nonzero league score on a new card!')
for phase in [1, 2]:
if phase == 1:
dummyscores = [
# An okay score on a chart
{
'id': 40000059,
'chart': 2,
'clear': True,
'fc': False,
'ex': False,
'score': 800000,
'expected_medal': 0x3,
},
# A good score on an easier chart of the same song
{
'id': 40000059,
'chart': 1,
'clear': True,
'fc': True,
'ex': False,
'score': 990000,
'expected_medal': 0x5,
},
# A perfect score on an easiest chart of the same song
{
'id': 40000059,
'chart': 0,
'clear': True,
'fc': True,
'ex': True,
'score': 1000000,
'expected_medal': 0x9,
},
# A bad score on a hard chart
{
'id': 30000024,
'chart': 2,
'clear': False,
'fc': False,
'ex': False,
'score': 400000,
'expected_medal': 0x1,
},
# A terrible score on an easy chart
{
'id': 50000045,
'chart': 0,
'clear': False,
'fc': False,
'ex': False,
'score': 100000,
'expected_medal': 0x1,
},
]
if phase == 2:
dummyscores = [
# A better score on the same chart
{
'id': 50000045,
'chart': 0,
'clear': True,
'fc': False,
'ex': False,
'score': 850000,
'expected_medal': 0x3,
},
# A worse score on another same chart
{
'id': 40000059,
'chart': 1,
'clear': True,
'fc': False,
'ex': False,
'score': 925000,
'expected_score': 990000,
'expected_medal': 0x7,
},
]
self.verify_gameend_regist(ref_id, jid, dummyscores, {}, None)
jid = self.verify_gametop_get_pdata(card, ref_id)
scores = self.verify_gametop_get_mdata(jid)
courses = self.verify_gametop_get_course(jid)
league = self.verify_gametop_get_league(jid)
if len(courses) > 0:
raise Exception('Got nonzero course count without playing any courses!')
if league[1][0] != 0 or league[1][1] != 0 or league[1][2] != 0:
raise Exception('Got nonzero league count without playing any league!')
for score in dummyscores:
newscore = scores[str(score['id'])][score['chart']]
if 'expected_score' in score:
expected_score = score['expected_score']
else:
expected_score = score['score']
if newscore['score'] != expected_score:
raise Exception(f'Expected a score of \'{expected_score}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got score \'{newscore["score"]}\'')
if newscore['medal'] != score['expected_medal']:
raise Exception(f'Expected a medal of \'{score["expected_medal"]}\' for song \'{score["id"]}\' chart \'{score["chart"]}\' but got medal \'{newscore["medal"]}\'')
# Sleep so we don't end up putting in score history on the same second
time.sleep(1)
for phase in [1, 2]:
dummycourses: List[Dict[str, Any]] = []
if phase == 1:
dummycourses.extend([
{
'course_id': 1,
'rating': 1,
'scores': [123456, 123457, 123458, 123459, 123460],
},
{
'course_id': 2,
'rating': 2,
'scores': [123456, 123457, 123458, 123459, 123460],
},
])
else:
dummycourses.extend([
{
'course_id': 1,
'rating': 2,
'scores': [223456, 223457, 223458, 223459, 223460],
},
{
'course_id': 2,
'rating': 1,
'expected_rating': 2,
'scores': [23456, 23457, 23458, 23459, 23460],
'expected_scores': [123456, 123457, 123458, 123459, 123460],
},
])
for course in dummycourses:
self.verify_gameend_regist(ref_id, jid, [], course, None)
jid = self.verify_gametop_get_pdata(card, ref_id)
courses = self.verify_gametop_get_course(jid)
for course in dummycourses:
# Find the course
foundcourses = [c for c in courses if c['course_id'] == course['course_id']]
if len(foundcourses) == 0:
raise Exception(f"Didn't find course by ID {course['course_id']}")
foundcourse = foundcourses[0]
if 'expected_rating' in course:
expected_rating = course['expected_rating']
else:
expected_rating = course['rating']
if 'expected_scores' in course:
expected_scores = course['expected_scores']
else:
expected_scores = course['scores']
if foundcourse['course_id'] != course['course_id']:
raise Exception("Logic error!")
if foundcourse['rating'] != expected_rating:
raise Exception(f'Expected a rating of \'{expected_rating}\' for course \'{course["course_id"]}\' but got rating \'{foundcourse["rating"]}\'')
for i in range(len(expected_scores)):
if foundcourse['scores'][i] != expected_scores[i]:
raise Exception(f'Expected a score of \'{expected_scores[i]}\' for course \'{course["course_id"]}\' song number \'{i}\' but got score \'{foundcourse["scores"][i]}\'')
# Sleep so we don't end up putting in score history on the same second
time.sleep(1)
# Play a league course, save the score
self.verify_gameend_regist(ref_id, jid, [], {}, (league[0], (123456, 234567, 345678)))
jid = self.verify_gametop_get_pdata(card, ref_id)
league = self.verify_gametop_get_league(jid)
if league[1][0] != 123456 or league[1][1] != 234567 or league[1][2] != 345678:
raise Exception(f'League score didn\t save! Got wrong values {league[1][0]}, {league[1][1]}, {league[1][2]} back!')
# Play a league course, do worse, make sure it doesn't overwrite
self.verify_gameend_regist(ref_id, jid, [], {}, (league[0], (12345, 23456, 34567)))
jid = self.verify_gametop_get_pdata(card, ref_id)
league = self.verify_gametop_get_league(jid)
if league[1][0] != 123456 or league[1][1] != 234567 or league[1][2] != 345678:
raise Exception(f'League score got overwritten! Got wrong values {league[1][0]}, {league[1][1]}, {league[1][2]} back!')
else:
print("Skipping score checks for existing card")
# 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)