509cb4f0d9
Exact commands run were: python3 -m libcst.tool codemod convert_format_to_fstring.ConvertFormatStringCommand . --no-format python3 setup.py build_ext --inplace
907 lines
40 KiB
Python
907 lines
40 KiB
Python
import random
|
||
import time
|
||
from typing import Dict, List, Optional
|
||
|
||
from bemani.common import Time
|
||
from bemani.client.base import BaseClient
|
||
from bemani.protocol import Node
|
||
|
||
|
||
class ReflecBeatLimelight(BaseClient):
|
||
NAME = 'TEST'
|
||
|
||
def verify_log_pcb_status(self, loc: str) -> None:
|
||
call = self.call_node()
|
||
|
||
pcb = Node.void('log')
|
||
pcb.set_attribute('method', 'pcb_status')
|
||
pcb.add_child(Node.string('lid', loc))
|
||
pcb.add_child(Node.s32('cnt', 0))
|
||
call.add_child(pcb)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/log/@status")
|
||
|
||
def verify_pcbinfo_get(self, loc: str) -> None:
|
||
call = self.call_node()
|
||
|
||
pcb = Node.void('pcbinfo')
|
||
pcb.set_attribute('method', 'get')
|
||
pcb.add_child(Node.string('lid', loc))
|
||
call.add_child(pcb)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/pcbinfo/info/name")
|
||
self.assert_path(resp, "response/pcbinfo/info/pref")
|
||
self.assert_path(resp, "response/pcbinfo/info/close")
|
||
self.assert_path(resp, "response/pcbinfo/info/hour")
|
||
self.assert_path(resp, "response/pcbinfo/info/min")
|
||
|
||
def verify_sysinfo_get(self) -> None:
|
||
call = self.call_node()
|
||
|
||
info = Node.void('sysinfo')
|
||
info.set_attribute('method', 'get')
|
||
call.add_child(info)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/sysinfo/trd")
|
||
|
||
def verify_ranking_read(self) -> None:
|
||
call = self.call_node()
|
||
|
||
ranking = Node.void('ranking')
|
||
ranking.set_attribute('method', 'read')
|
||
call.add_child(ranking)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/ranking/lic_10/time")
|
||
self.assert_path(resp, "response/ranking/org_10/time")
|
||
|
||
def verify_player_start(self, refid: str) -> None:
|
||
call = self.call_node()
|
||
|
||
player = Node.void('player')
|
||
player.set_attribute('method', 'start')
|
||
player.add_child(Node.string('rid', refid))
|
||
player.add_child(Node.s32('ver', 0))
|
||
call.add_child(player)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/player/is_suc")
|
||
self.assert_path(resp, "response/player/unlock_music")
|
||
self.assert_path(resp, "response/player/unlock_item")
|
||
self.assert_path(resp, "response/player/item_lock_ctrl")
|
||
self.assert_path(resp, "response/player/lincle_link_4/qpro")
|
||
self.assert_path(resp, "response/player/lincle_link_4/glass")
|
||
self.assert_path(resp, "response/player/lincle_link_4/treasure")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_0")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_1")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_2")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_3")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_4")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_5")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0_6")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_0")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_1")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_2")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_3")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_iidx_4")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_0")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_1")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_2")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_3")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_4")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_5")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0_6")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_0")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_1")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_2")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_3")
|
||
self.assert_path(resp, "response/player/lincle_link_4/for_rb_4")
|
||
self.assert_path(resp, "response/player/lincle_link_4/qproflg")
|
||
self.assert_path(resp, "response/player/lincle_link_4/glassflg")
|
||
self.assert_path(resp, "response/player/lincle_link_4/complete")
|
||
|
||
def verify_player_delete(self, refid: str) -> None:
|
||
call = self.call_node()
|
||
|
||
player = Node.void('player')
|
||
player.set_attribute('method', 'delete')
|
||
player.add_child(Node.string('rid', refid))
|
||
call.add_child(player)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/player/@status")
|
||
|
||
def verify_player_end(self, refid: str) -> None:
|
||
call = self.call_node()
|
||
|
||
player = Node.void('player')
|
||
player.set_attribute('method', 'end')
|
||
player.add_child(Node.string('rid', refid))
|
||
player.add_child(Node.s32('status', 4))
|
||
call.add_child(player)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/player")
|
||
|
||
def verify_player_read(self, refid: str, location: str) -> List[Dict[str, int]]:
|
||
call = self.call_node()
|
||
|
||
player = Node.void('player')
|
||
player.set_attribute('method', 'read')
|
||
player.add_child(Node.string('rid', refid))
|
||
player.add_child(Node.string('lid', location))
|
||
player.add_child(Node.s32('ver', 2))
|
||
call.add_child(player)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/player/pdata/base/uid")
|
||
self.assert_path(resp, "response/player/pdata/base/name")
|
||
self.assert_path(resp, "response/player/pdata/base/icon_id")
|
||
self.assert_path(resp, "response/player/pdata/base/lv")
|
||
self.assert_path(resp, "response/player/pdata/base/exp")
|
||
self.assert_path(resp, "response/player/pdata/base/mg")
|
||
self.assert_path(resp, "response/player/pdata/base/ap")
|
||
self.assert_path(resp, "response/player/pdata/base/pc")
|
||
self.assert_path(resp, "response/player/pdata/base/uattr")
|
||
self.assert_path(resp, "response/player/pdata/con/day")
|
||
self.assert_path(resp, "response/player/pdata/con/cnt")
|
||
self.assert_path(resp, "response/player/pdata/con/total_cnt")
|
||
self.assert_path(resp, "response/player/pdata/con/last")
|
||
self.assert_path(resp, "response/player/pdata/con/now")
|
||
self.assert_path(resp, "response/player/pdata/team/id")
|
||
self.assert_path(resp, "response/player/pdata/team/name")
|
||
self.assert_path(resp, "response/player/pdata/custom/s_gls")
|
||
self.assert_path(resp, "response/player/pdata/custom/bgm_m")
|
||
self.assert_path(resp, "response/player/pdata/custom/st_f")
|
||
self.assert_path(resp, "response/player/pdata/custom/st_bg")
|
||
self.assert_path(resp, "response/player/pdata/custom/st_bg_b")
|
||
self.assert_path(resp, "response/player/pdata/custom/eff_e")
|
||
self.assert_path(resp, "response/player/pdata/custom/se_s")
|
||
self.assert_path(resp, "response/player/pdata/custom/se_s_v")
|
||
self.assert_path(resp, "response/player/pdata/custom/last_music_id")
|
||
self.assert_path(resp, "response/player/pdata/custom/last_note_grade")
|
||
self.assert_path(resp, "response/player/pdata/custom/sort_type")
|
||
self.assert_path(resp, "response/player/pdata/custom/narrowdown_type")
|
||
self.assert_path(resp, "response/player/pdata/custom/is_begginer")
|
||
self.assert_path(resp, "response/player/pdata/custom/is_tut")
|
||
self.assert_path(resp, "response/player/pdata/custom/symbol_chat_0")
|
||
self.assert_path(resp, "response/player/pdata/custom/symbol_chat_1")
|
||
self.assert_path(resp, "response/player/pdata/custom/gauge_style")
|
||
self.assert_path(resp, "response/player/pdata/custom/obj_shade")
|
||
self.assert_path(resp, "response/player/pdata/custom/obj_size")
|
||
self.assert_path(resp, "response/player/pdata/custom/byword")
|
||
self.assert_path(resp, "response/player/pdata/custom/is_auto_byword")
|
||
self.assert_path(resp, "response/player/pdata/custom/is_tweet")
|
||
self.assert_path(resp, "response/player/pdata/custom/is_link_twitter")
|
||
self.assert_path(resp, "response/player/pdata/custom/mrec_type")
|
||
self.assert_path(resp, "response/player/pdata/custom/card_disp_type")
|
||
self.assert_path(resp, "response/player/pdata/custom/tab_sel")
|
||
self.assert_path(resp, "response/player/pdata/custom/hidden_param")
|
||
self.assert_path(resp, "response/player/pdata/released")
|
||
self.assert_path(resp, "response/player/pdata/record")
|
||
self.assert_path(resp, "response/player/pdata/cmnt")
|
||
self.assert_path(resp, "response/player/pdata/rival")
|
||
self.assert_path(resp, "response/player/pdata/glass")
|
||
self.assert_path(resp, "response/player/pdata/fav_music_slot")
|
||
self.assert_path(resp, "response/player/pdata/narrow_down/adv_param")
|
||
|
||
if resp.child_value('player/pdata/base/name') != self.NAME:
|
||
raise Exception(f'Invalid name {resp.child_value("player/pdata/base/name")} returned on profile read!')
|
||
|
||
scores = []
|
||
for child in resp.child('player/pdata/record').children:
|
||
if child.name != 'rec':
|
||
continue
|
||
|
||
score = {
|
||
'id': child.child_value('mid'),
|
||
'chart': child.child_value('ng'),
|
||
'clear_type': child.child_value('mrec_0/ct'),
|
||
'achievement_rate': child.child_value('mrec_0/ar'),
|
||
'score': child.child_value('mrec_0/bs'),
|
||
'combo': child.child_value('mrec_0/mc'),
|
||
'miss_count': child.child_value('mrec_0/bmc'),
|
||
}
|
||
scores.append(score)
|
||
return scores
|
||
|
||
def verify_player_write(self, refid: str, extid: int, loc: str, records: List[Dict[str, int]], scores: List[Dict[str, int]]) -> int:
|
||
call = self.call_node()
|
||
|
||
player = Node.void('player')
|
||
call.add_child(player)
|
||
player.set_attribute('method', 'write')
|
||
player.add_child(Node.string('rid', refid))
|
||
player.add_child(Node.string('lid', loc))
|
||
player.add_child(Node.u64('begin_time', Time.now() * 1000))
|
||
player.add_child(Node.u64('end_time', Time.now() * 1000))
|
||
pdata = Node.void('pdata')
|
||
player.add_child(pdata)
|
||
base = Node.void('base')
|
||
pdata.add_child(base)
|
||
base.add_child(Node.s32('uid', extid))
|
||
base.add_child(Node.string('name', self.NAME))
|
||
base.add_child(Node.s16('icon_id', 0))
|
||
base.add_child(Node.s16('lv', 1))
|
||
base.add_child(Node.s32('exp', 0))
|
||
base.add_child(Node.s16('mg', 0))
|
||
base.add_child(Node.s16('ap', 0))
|
||
base.add_child(Node.s32('pc', 0))
|
||
base.add_child(Node.s32('uattr', 0))
|
||
con = Node.void('con')
|
||
pdata.add_child(con)
|
||
con.add_child(Node.s32('day', 0))
|
||
con.add_child(Node.s32('cnt', 0))
|
||
con.add_child(Node.s32('total_cnt', 0))
|
||
con.add_child(Node.s32('last', 0))
|
||
con.add_child(Node.s32('now', 0))
|
||
custom = Node.void('custom')
|
||
pdata.add_child(custom)
|
||
custom.add_child(Node.u8('s_gls', 0))
|
||
custom.add_child(Node.u8('bgm_m', 0))
|
||
custom.add_child(Node.u8('st_f', 0))
|
||
custom.add_child(Node.u8('st_bg', 0))
|
||
custom.add_child(Node.u8('st_bg_b', 100))
|
||
custom.add_child(Node.u8('eff_e', 0))
|
||
custom.add_child(Node.u8('se_s', 0))
|
||
custom.add_child(Node.u8('se_s_v', 100))
|
||
custom.add_child(Node.s16('last_music_id', 85))
|
||
custom.add_child(Node.u8('last_note_grade', 0))
|
||
custom.add_child(Node.u8('sort_type', 0))
|
||
custom.add_child(Node.u8('narrowdown_type', 0))
|
||
custom.add_child(Node.bool('is_begginer', False))
|
||
custom.add_child(Node.bool('is_tut', False))
|
||
custom.add_child(Node.s16_array('symbol_chat_0', [0, 1, 2, 3, 4, 5]))
|
||
custom.add_child(Node.s16_array('symbol_chat_1', [0, 1, 2, 3, 4, 5]))
|
||
custom.add_child(Node.u8('gauge_style', 0))
|
||
custom.add_child(Node.u8('obj_shade', 0))
|
||
custom.add_child(Node.u8('obj_size', 0))
|
||
custom.add_child(Node.s16_array('byword', [0, 0]))
|
||
custom.add_child(Node.bool_array('is_auto_byword', [True, True]))
|
||
custom.add_child(Node.bool('is_tweet', False))
|
||
custom.add_child(Node.bool('is_link_twitter', False))
|
||
custom.add_child(Node.s16('mrec_type', 0))
|
||
custom.add_child(Node.s16('card_disp_type', 0))
|
||
custom.add_child(Node.s16('tab_sel', 0))
|
||
custom.add_child(Node.s32_array('hidden_param', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]))
|
||
pdata.add_child(Node.void('released'))
|
||
pdata.add_child(Node.void('rival'))
|
||
pdata.add_child(Node.void('glass'))
|
||
pdata.add_child(Node.void('fav_music_slot'))
|
||
lincle_link_4 = Node.void('lincle_link_4')
|
||
pdata.add_child(lincle_link_4)
|
||
lincle_link_4.add_child(Node.u32('qpro_add', 0))
|
||
lincle_link_4.add_child(Node.u32('glass_add', 0))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_0', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_1', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_2', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_3', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_4', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_5', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0_6', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_0', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_1', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_2', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_3', False))
|
||
lincle_link_4.add_child(Node.bool('for_iidx_4', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_0', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_1', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_2', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_3', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_4', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_5', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0_6', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_0', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_1', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_2', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_3', False))
|
||
lincle_link_4.add_child(Node.bool('for_rb_4', False))
|
||
|
||
# First, filter down to only records that are also in the battle log
|
||
def key(thing: Dict[str, int]) -> str:
|
||
return f'{thing["id"]}-{thing["chart"]}'
|
||
|
||
updates = [key(score) for score in scores]
|
||
sortedrecords = {key(record): record for record in records if key(record) in updates}
|
||
|
||
# Now, see what records need updating and update them
|
||
for score in scores:
|
||
if key(score) in sortedrecords:
|
||
# Had a record, need to merge
|
||
record = sortedrecords[key(score)]
|
||
else:
|
||
# First time playing
|
||
record = {
|
||
'clear_type': 0,
|
||
'achievement_rate': 0,
|
||
'score': 0,
|
||
'combo': 0,
|
||
'miss_count': 999999999,
|
||
}
|
||
|
||
sortedrecords[key(score)] = {
|
||
'id': score['id'],
|
||
'chart': score['chart'],
|
||
'clear_type': max(record['clear_type'], score['clear_type']),
|
||
'achievement_rate': max(record['achievement_rate'], score['achievement_rate']),
|
||
'score': max(record['score'], score['score']),
|
||
'combo': max(record['combo'], score['combo']),
|
||
'miss_count': min(record['miss_count'], score['miss_count']),
|
||
}
|
||
|
||
# Finally, send the records and battle logs
|
||
recordnode = Node.void('record')
|
||
pdata.add_child(recordnode)
|
||
blog = Node.void('blog')
|
||
pdata.add_child(blog)
|
||
|
||
for (_, record) in sortedrecords.items():
|
||
rec = Node.void('rec')
|
||
recordnode.add_child(rec)
|
||
rec.add_child(Node.u16('mid', record['id']))
|
||
rec.add_child(Node.u8('ng', record['chart']))
|
||
rec.add_child(Node.s32('point', 2))
|
||
rec.add_child(Node.s32('played_time', Time.now()))
|
||
mrec_0 = Node.void('mrec_0')
|
||
rec.add_child(mrec_0)
|
||
mrec_0.add_child(Node.s32('win', 1))
|
||
mrec_0.add_child(Node.s32('lose', 0))
|
||
mrec_0.add_child(Node.s32('draw', 0))
|
||
mrec_0.add_child(Node.u8('ct', record['clear_type']))
|
||
mrec_0.add_child(Node.s16('ar', record['achievement_rate']))
|
||
mrec_0.add_child(Node.s32('bs', record['score']))
|
||
mrec_0.add_child(Node.s16('mc', record['combo']))
|
||
mrec_0.add_child(Node.s16('bmc', record['miss_count']))
|
||
mrec_1 = Node.void('mrec_1')
|
||
rec.add_child(mrec_1)
|
||
mrec_1.add_child(Node.s32('win', 0))
|
||
mrec_1.add_child(Node.s32('lose', 0))
|
||
mrec_1.add_child(Node.s32('draw', 0))
|
||
mrec_1.add_child(Node.u8('ct', 0))
|
||
mrec_1.add_child(Node.s16('ar', 0))
|
||
mrec_1.add_child(Node.s32('bs', 0))
|
||
mrec_1.add_child(Node.s16('mc', 0))
|
||
mrec_1.add_child(Node.s16('bmc', -1))
|
||
|
||
scoreid = 0
|
||
for score in scores:
|
||
log = Node.void('log')
|
||
blog.add_child(log)
|
||
log.add_child(Node.u8('id', scoreid))
|
||
log.add_child(Node.u16('mid', score['id']))
|
||
log.add_child(Node.u8('ng', score['chart']))
|
||
log.add_child(Node.u8('mt', 0))
|
||
log.add_child(Node.u8('rt', 0))
|
||
log.add_child(Node.s32('ruid', 0))
|
||
myself = Node.void('myself')
|
||
log.add_child(myself)
|
||
myself.add_child(Node.s16('mg', 0))
|
||
myself.add_child(Node.s16('ap', 0))
|
||
myself.add_child(Node.u8('ct', score['clear_type']))
|
||
myself.add_child(Node.s32('s', score['score']))
|
||
myself.add_child(Node.s16('ar', score['achievement_rate']))
|
||
rival = Node.void('rival')
|
||
log.add_child(rival)
|
||
rival.add_child(Node.s16('mg', 0))
|
||
rival.add_child(Node.s16('ap', 0))
|
||
rival.add_child(Node.u8('ct', 2))
|
||
rival.add_child(Node.s32('s', 177))
|
||
rival.add_child(Node.s16('ar', 500))
|
||
log.add_child(Node.s32('time', Time.now()))
|
||
scoreid = scoreid + 1
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/player/uid")
|
||
self.assert_path(resp, "response/player/time")
|
||
return resp.child_value('player/uid')
|
||
|
||
def verify_log_play(self, extid: int, loc: str, scores: List[Dict[str, int]]) -> None:
|
||
call = self.call_node()
|
||
|
||
log = Node.void('log')
|
||
call.add_child(log)
|
||
log.set_attribute('method', 'play')
|
||
log.add_child(Node.s32('uid', extid))
|
||
log.add_child(Node.string('lid', loc))
|
||
play = Node.void('play')
|
||
log.add_child(play)
|
||
play.add_child(Node.s16('stage', len(scores)))
|
||
play.add_child(Node.s32('sec', 700))
|
||
|
||
scoreid = 0
|
||
for score in scores:
|
||
rec = Node.void('rec')
|
||
log.add_child(rec)
|
||
rec.add_child(Node.s16('idx', scoreid))
|
||
rec.add_child(Node.s16('mid', score['id']))
|
||
rec.add_child(Node.s16('grade', score['chart']))
|
||
rec.add_child(Node.s16('color', 0))
|
||
rec.add_child(Node.s16('match', 0))
|
||
rec.add_child(Node.s16('res', 0))
|
||
rec.add_child(Node.s32('score', score['score']))
|
||
rec.add_child(Node.s16('mc', score['combo']))
|
||
rec.add_child(Node.s16('jt_jr', 0))
|
||
rec.add_child(Node.s16('jt_ju', 0))
|
||
rec.add_child(Node.s16('jt_gr', 0))
|
||
rec.add_child(Node.s16('jt_gd', 0))
|
||
rec.add_child(Node.s16('jt_ms', score['miss_count']))
|
||
rec.add_child(Node.s32('sec', 200))
|
||
scoreid = scoreid + 1
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/log/@status")
|
||
|
||
def verify_lobby_read(self, location: str, extid: int) -> None:
|
||
call = self.call_node()
|
||
|
||
lobby = Node.void('lobby')
|
||
lobby.set_attribute('method', 'read')
|
||
lobby.add_child(Node.s32('uid', extid))
|
||
lobby.add_child(Node.u8('m_grade', 255))
|
||
lobby.add_child(Node.string('lid', location))
|
||
lobby.add_child(Node.s32('max', 128))
|
||
lobby.add_child(Node.s32_array('friend', []))
|
||
call.add_child(lobby)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/lobby/interval")
|
||
self.assert_path(resp, "response/lobby/interval_p")
|
||
|
||
def verify_lobby_entry(self, location: str, extid: int) -> int:
|
||
call = self.call_node()
|
||
|
||
lobby = Node.void('lobby')
|
||
lobby.set_attribute('method', 'entry')
|
||
e = Node.void('e')
|
||
lobby.add_child(e)
|
||
e.add_child(Node.s32('eid', 0))
|
||
e.add_child(Node.u16('mid', 79))
|
||
e.add_child(Node.u8('ng', 0))
|
||
e.add_child(Node.s32('uid', extid))
|
||
e.add_child(Node.s32('uattr', 0))
|
||
e.add_child(Node.string('pn', self.NAME))
|
||
e.add_child(Node.s16('mg', 0))
|
||
e.add_child(Node.s32('mopt', 0))
|
||
e.add_child(Node.s32('tid', 0))
|
||
e.add_child(Node.string('tn', ''))
|
||
e.add_child(Node.s32('topt', 0))
|
||
e.add_child(Node.string('lid', location))
|
||
e.add_child(Node.string('sn', ''))
|
||
e.add_child(Node.u8('pref', 51))
|
||
e.add_child(Node.s8('stg', 0))
|
||
e.add_child(Node.s8('pside', 0))
|
||
e.add_child(Node.s16('eatime', 30))
|
||
e.add_child(Node.u8_array('ga', [127, 0, 0, 1]))
|
||
e.add_child(Node.u16('gp', 10007))
|
||
e.add_child(Node.u8_array('la', [16, 0, 0, 0]))
|
||
lobby.add_child(Node.s32_array('friend', []))
|
||
call.add_child(lobby)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/lobby/eid")
|
||
self.assert_path(resp, "response/lobby/interval")
|
||
self.assert_path(resp, "response/lobby/interval_p")
|
||
self.assert_path(resp, "response/lobby/e/eid")
|
||
self.assert_path(resp, "response/lobby/e/mid")
|
||
self.assert_path(resp, "response/lobby/e/ng")
|
||
self.assert_path(resp, "response/lobby/e/uid")
|
||
self.assert_path(resp, "response/lobby/e/pn")
|
||
self.assert_path(resp, "response/lobby/e/uattr")
|
||
self.assert_path(resp, "response/lobby/e/mopt")
|
||
self.assert_path(resp, "response/lobby/e/mg")
|
||
self.assert_path(resp, "response/lobby/e/tid")
|
||
self.assert_path(resp, "response/lobby/e/tn")
|
||
self.assert_path(resp, "response/lobby/e/topt")
|
||
self.assert_path(resp, "response/lobby/e/lid")
|
||
self.assert_path(resp, "response/lobby/e/sn")
|
||
self.assert_path(resp, "response/lobby/e/pref")
|
||
self.assert_path(resp, "response/lobby/e/stg")
|
||
self.assert_path(resp, "response/lobby/e/pside")
|
||
self.assert_path(resp, "response/lobby/e/eatime")
|
||
self.assert_path(resp, "response/lobby/e/ga")
|
||
self.assert_path(resp, "response/lobby/e/gp")
|
||
self.assert_path(resp, "response/lobby/e/la")
|
||
return resp.child_value('lobby/eid')
|
||
|
||
def verify_lobby_delete(self, eid: int) -> None:
|
||
call = self.call_node()
|
||
|
||
lobby = Node.void('lobby')
|
||
lobby.set_attribute('method', 'delete')
|
||
lobby.add_child(Node.s32('eid', eid))
|
||
call.add_child(lobby)
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/lobby")
|
||
|
||
def verify_event_w_update_status(self, loc: str, extid: int) -> None:
|
||
call = self.call_node()
|
||
|
||
event_w = Node.void('event_w')
|
||
call.add_child(event_w)
|
||
event_w.set_attribute('method', 'update_status')
|
||
event_w.add_child(Node.s32('uid', extid))
|
||
event_w.add_child(Node.string('p_name', self.NAME))
|
||
event_w.add_child(Node.s32('exp', 0))
|
||
event_w.add_child(Node.s32('customize', 0))
|
||
event_w.add_child(Node.s32('tid', 0))
|
||
event_w.add_child(Node.string('t_name', ''))
|
||
event_w.add_child(Node.string('lid', loc))
|
||
event_w.add_child(Node.string('s_name', ''))
|
||
event_w.add_child(Node.s8('pref', 51))
|
||
event_w.add_child(Node.s32('time', Time.now()))
|
||
event_w.add_child(Node.s8('status', 1))
|
||
event_w.add_child(Node.s8('stage', 0))
|
||
event_w.add_child(Node.s32('mid', -1))
|
||
event_w.add_child(Node.s8('ng', -1))
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/event_w/@status")
|
||
|
||
def verify_event_w_add_comment(self, loc: str, extid: int) -> None:
|
||
call = self.call_node()
|
||
|
||
event_w = Node.void('event_w')
|
||
call.add_child(event_w)
|
||
event_w.set_attribute('method', 'add_comment')
|
||
event_w.add_child(Node.s32('uid', extid))
|
||
event_w.add_child(Node.string('p_name', self.NAME))
|
||
event_w.add_child(Node.s32('exp', 0))
|
||
event_w.add_child(Node.s32('customize', 0))
|
||
event_w.add_child(Node.s32('tid', 0))
|
||
event_w.add_child(Node.string('t_name', ''))
|
||
event_w.add_child(Node.string('lid', loc))
|
||
event_w.add_child(Node.string('s_name', ''))
|
||
event_w.add_child(Node.s8('pref', 51))
|
||
event_w.add_child(Node.s32('time', Time.now()))
|
||
event_w.add_child(Node.string('comment', 'アメ〜〜!'))
|
||
event_w.add_child(Node.bool('is_tweet', False))
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct
|
||
self.assert_path(resp, "response/event_w/@status")
|
||
|
||
def verify_event_r_get_all(self, extid: int) -> None:
|
||
call = self.call_node()
|
||
|
||
event_r = Node.void('event_r')
|
||
call.add_child(event_r)
|
||
event_r.set_attribute('method', 'get_all')
|
||
event_r.add_child(Node.s32('uid', extid))
|
||
event_r.add_child(Node.s32('time', 0))
|
||
event_r.add_child(Node.s32('limit', 30))
|
||
|
||
# Swap with server
|
||
resp = self.exchange('', call)
|
||
|
||
# Verify that response is correct. We should have at least one
|
||
# comment (the one we just wrote) and one status (because we just
|
||
# called update_status).
|
||
self.assert_path(resp, "response/event_r/time")
|
||
self.assert_path(resp, "response/event_r/status/s/uid")
|
||
self.assert_path(resp, "response/event_r/status/s/p_name")
|
||
self.assert_path(resp, "response/event_r/status/s/exp")
|
||
self.assert_path(resp, "response/event_r/status/s/customize")
|
||
self.assert_path(resp, "response/event_r/status/s/tid")
|
||
self.assert_path(resp, "response/event_r/status/s/t_name")
|
||
self.assert_path(resp, "response/event_r/status/s/lid")
|
||
self.assert_path(resp, "response/event_r/status/s/s_name")
|
||
self.assert_path(resp, "response/event_r/status/s/pref")
|
||
self.assert_path(resp, "response/event_r/status/s/time")
|
||
self.assert_path(resp, "response/event_r/status/s/status")
|
||
self.assert_path(resp, "response/event_r/status/s/stage")
|
||
self.assert_path(resp, "response/event_r/status/s/mid")
|
||
self.assert_path(resp, "response/event_r/status/s/ng")
|
||
self.assert_path(resp, "response/event_r/comment/c/uid")
|
||
self.assert_path(resp, "response/event_r/comment/c/p_name")
|
||
self.assert_path(resp, "response/event_r/comment/c/exp")
|
||
self.assert_path(resp, "response/event_r/comment/c/customize")
|
||
self.assert_path(resp, "response/event_r/comment/c/tid")
|
||
self.assert_path(resp, "response/event_r/comment/c/t_name")
|
||
self.assert_path(resp, "response/event_r/comment/c/lid")
|
||
self.assert_path(resp, "response/event_r/comment/c/s_name")
|
||
self.assert_path(resp, "response/event_r/comment/c/pref")
|
||
self.assert_path(resp, "response/event_r/comment/c/time")
|
||
self.assert_path(resp, "response/event_r/comment/c/comment")
|
||
self.assert_path(resp, "response/event_r/comment/c/is_tweet")
|
||
|
||
# Verify we posted our comment earlier
|
||
found = False
|
||
for child in resp.child('event_r/comment').children:
|
||
if child.name != 'c':
|
||
continue
|
||
if child.child_value('uid') == extid:
|
||
name = child.child_value('p_name')
|
||
comment = child.child_value('comment')
|
||
if name != self.NAME:
|
||
raise Exception(f'Invalid name \'{name}\' returned for comment!')
|
||
if comment != 'アメ〜〜!':
|
||
raise Exception(f'Invalid comment \'{comment}\' returned for comment!')
|
||
found = True
|
||
|
||
if not found:
|
||
raise Exception('Comment we posted was not found!')
|
||
|
||
# Verify our status came through
|
||
found = False
|
||
for child in resp.child('event_r/status').children:
|
||
if child.name != 's':
|
||
continue
|
||
if child.child_value('uid') == extid:
|
||
name = child.child_value('p_name')
|
||
if name != self.NAME:
|
||
raise Exception(f'Invalid name \'{name}\' returned for status!')
|
||
found = True
|
||
|
||
if not found:
|
||
raise Exception('Status was not found!')
|
||
|
||
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_log_pcb_status(location)
|
||
self.verify_pcbinfo_get(location)
|
||
|
||
self.verify_sysinfo_get()
|
||
self.verify_ranking_read()
|
||
|
||
# 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')
|
||
# Always get a player start, regardless of new profile or not
|
||
self.verify_player_start(ref_id)
|
||
self.verify_player_delete(ref_id)
|
||
extid = self.verify_player_write(
|
||
ref_id,
|
||
0,
|
||
location,
|
||
[],
|
||
[],
|
||
)
|
||
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 lobby functionality
|
||
self.verify_lobby_read(location, extid)
|
||
eid = self.verify_lobby_entry(location, extid)
|
||
self.verify_lobby_delete(eid)
|
||
|
||
# Verify status updates and puzzle comments
|
||
self.verify_event_w_update_status(location, extid)
|
||
self.verify_event_w_add_comment(location, extid)
|
||
self.verify_event_r_get_all(extid)
|
||
|
||
# Limelight is weird and sends only the top record for each song you played,
|
||
# and then a separate battle log. So, emulating that is kinda hard.
|
||
scores: List[Dict[str, int]] = []
|
||
if cardid is None:
|
||
# Verify score saving and updating
|
||
for phase in [1, 2]:
|
||
if phase == 1:
|
||
dummyscores = [
|
||
# An okay score on a chart
|
||
{
|
||
'id': 1,
|
||
'chart': 1,
|
||
'clear_type': 2,
|
||
'achievement_rate': 7543,
|
||
'score': 432,
|
||
'combo': 123,
|
||
'miss_count': 5,
|
||
},
|
||
# A good score on an easier chart of the same song
|
||
{
|
||
'id': 1,
|
||
'chart': 0,
|
||
'clear_type': 3,
|
||
'achievement_rate': 9876,
|
||
'score': 543,
|
||
'combo': 543,
|
||
'miss_count': 0,
|
||
},
|
||
# A bad score on a hard chart
|
||
{
|
||
'id': 3,
|
||
'chart': 2,
|
||
'clear_type': 2,
|
||
'achievement_rate': 1234,
|
||
'score': 123,
|
||
'combo': 42,
|
||
'miss_count': 54,
|
||
},
|
||
# A terrible score on an easy chart
|
||
{
|
||
'id': 3,
|
||
'chart': 0,
|
||
'clear_type': 2,
|
||
'achievement_rate': 1024,
|
||
'score': 50,
|
||
'combo': 12,
|
||
'miss_count': 90,
|
||
},
|
||
]
|
||
if phase == 2:
|
||
dummyscores = [
|
||
# A better score on the same chart
|
||
{
|
||
'id': 1,
|
||
'chart': 1,
|
||
'clear_type': 3,
|
||
'achievement_rate': 8765,
|
||
'score': 469,
|
||
'combo': 468,
|
||
'miss_count': 1,
|
||
},
|
||
# A worse score on another same chart
|
||
{
|
||
'id': 1,
|
||
'chart': 0,
|
||
'clear_type': 2,
|
||
'achievement_rate': 8765,
|
||
'score': 432,
|
||
'combo': 321,
|
||
'miss_count': 15,
|
||
'expected_score': 543,
|
||
'expected_clear_type': 3,
|
||
'expected_achievement_rate': 9876,
|
||
'expected_combo': 543,
|
||
'expected_miss_count': 0,
|
||
},
|
||
]
|
||
self.verify_player_write(ref_id, extid, location, scores, dummyscores)
|
||
self.verify_log_play(extid, location, dummyscores)
|
||
|
||
scores = self.verify_player_read(ref_id, location)
|
||
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_achievement_rate' in expected:
|
||
expected_achievement_rate = expected['expected_achievement_rate']
|
||
else:
|
||
expected_achievement_rate = expected['achievement_rate']
|
||
if 'expected_clear_type' in expected:
|
||
expected_clear_type = expected['expected_clear_type']
|
||
else:
|
||
expected_clear_type = expected['clear_type']
|
||
if 'expected_combo' in expected:
|
||
expected_combo = expected['expected_combo']
|
||
else:
|
||
expected_combo = expected['combo']
|
||
if 'expected_miss_count' in expected:
|
||
expected_miss_count = expected['expected_miss_count']
|
||
else:
|
||
expected_miss_count = expected['miss_count']
|
||
|
||
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['achievement_rate'] != expected_achievement_rate:
|
||
raise Exception(f'Expected an achievement rate of \'{expected_achievement_rate}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got achievement rate \'{actual["achievement_rate"]}\'')
|
||
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"]}\'')
|
||
if actual['combo'] != expected_combo:
|
||
raise Exception(f'Expected a combo of \'{expected_combo}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got combo \'{actual["combo"]}\'')
|
||
if actual['miss_count'] != expected_miss_count:
|
||
raise Exception(f'Expected a miss count of \'{expected_miss_count}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got miss count \'{actual["miss_count"]}\'')
|
||
|
||
# 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 ending game
|
||
self.verify_player_end(ref_id)
|
||
|
||
# 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)
|