1
0
mirror of synced 2024-11-24 06:20:12 +01:00

fix issues and verify lint

This commit is contained in:
Shinrin Ouja Moriking 2024-04-01 00:10:37 -06:00
parent 2b6ce21084
commit cd587b19c3
12 changed files with 243 additions and 140 deletions

View File

@ -4,4 +4,4 @@ from bemani.backend.hellopopn.base import HelloPopnBase
__all__ = [
"HelloPopnFactory",
"HelloPopnBase",
]
]

View File

@ -1,9 +1,7 @@
from typing import Optional
from typing_extensions import Final
from bemani.backend.base import Base
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
from bemani.common import GameConstants, ValidatedDict
from bemani.protocol import Node
from bemani.common import GameConstants
class HelloPopnBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):

View File

@ -24,4 +24,4 @@ class HelloPopnFactory(Factory):
return HelloPopnMusic(data, config, model)
# Unknown game version
return None
return None

View File

@ -5,9 +5,10 @@ from typing import Any, Dict
from bemani.backend.hellopopn.base import HelloPopnBase
from bemani.backend.ess import EventLogHandler
from bemani.common import ValidatedDict, VersionConstants, Profile
from bemani.data import Score
from bemani.data import Score
from bemani.protocol import Node
class HelloPopnMusic(
EventLogHandler,
HelloPopnBase,
@ -16,14 +17,11 @@ class HelloPopnMusic(
version = VersionConstants.HELLO_POPN_MUSIC
@classmethod
def get_settings(cls) -> Dict[str, Any]:
"""
Return all of our front-end modifiably settings.
"""
return {
"ints": [
],
'bools': [
{
'name': 'Force Song Unlock',
@ -46,18 +44,18 @@ class HelloPopnMusic(
flag.set_attribute("t", '1')
root.add_child(Node.u32("cnt_music", 36))
return root
def handle_game_shop_request(self, request: Node) -> Node:
root = Node.void('game')
return root
def handle_game_new_request(self, request: Node) -> Node:
#profile creation
# profile creation
root = Node.void('game')
userid = self.data.remote.user.from_refid(self.game, self.version, request.attribute('refid'))
defaultprofile = Profile(
@ -66,12 +64,12 @@ class HelloPopnMusic(
request.attribute('refid'),
0,
{
'name': "なし",
'chara': "0",
'music_id': "0",
'level': "0",
'style': "0",
'love': "0"
'name': "なし",
'chara': "0",
'music_id': "0",
'level': "0",
'style': "0",
'love': "0"
},
)
self.put_profile(userid, defaultprofile)
@ -79,33 +77,31 @@ class HelloPopnMusic(
return root
def handle_game_load_request(self, request: Node) -> Node:
#Load profile values
# Load profile values
root = Node.void('game')
userid = self.data.remote.user.from_refid(self.game, self.version, request.attribute('refid'))
profile = self.get_profile(userid)
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
game_config = self.get_game_config()
force_unlock_songs = game_config.get_bool("force_unlock_songs")
#if we send all chara love as max, all songs will be unlocked
# if we send all chara love as max, all songs will be unlocked
if force_unlock_songs:
for n in range(12):
chara = Node.void('chara')
chara.set_attribute('id', str(n))
chara.set_attribute('love', "5")
root.add_child(chara)
for n in range(12):
chara = Node.void('chara')
chara.set_attribute('id', str(n))
chara.set_attribute('love', "5")
root.add_child(chara)
else:
#load chara love progress
for achievement in achievements:
if achievement.type == 'toki_love':
chara = Node.void('chara')
chara.set_attribute('id', str(achievement.id))
chara.set_attribute('love', achievement.data.get_str('love'))
root.add_child(chara)
# load chara love progress
for achievement in achievements:
if achievement.type == 'toki_love':
chara = Node.void('chara')
chara.set_attribute('id', str(achievement.id))
chara.set_attribute('love', achievement.data.get_str('love'))
root.add_child(chara)
last = Node.void('last')
root.add_child(last)
@ -115,11 +111,11 @@ class HelloPopnMusic(
last.set_attribute('style', profile.get_str('style'))
self.update_play_statistics(userid)
return root
def handle_game_load_m_request(self, request: Node) -> Node:
#Load scores
# Load scores
userid = self.data.remote.user.from_refid(self.game, self.version, request.attribute('refid'))
scores = self.data.remote.music.get_scores(self.game, self.version, userid)
@ -129,7 +125,7 @@ class HelloPopnMusic(
if score.id not in sortedscores:
sortedscores[score.id] = {}
sortedscores[score.id][score.chart] = score
for song in sortedscores:
for chart in sortedscores[song]:
score = sortedscores[song][chart]
@ -152,7 +148,7 @@ class HelloPopnMusic(
return root
def handle_game_save_request(self, request: Node) -> Node:
#Save profile data
# Save profile data
root = Node.void('game')
userid = self.data.remote.user.from_refid(self.game, self.version, request.attribute('refid'))
@ -169,35 +165,32 @@ class HelloPopnMusic(
self.put_profile(userid, newprofile)
game_config = self.get_game_config()
force_unlock_songs = game_config.get_bool("force_unlock_songs")
#if we were on force unlock mode, achievements will not be modified
# if we were on force unlock mode, achievements will not be modified
if force_unlock_songs is False:
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
love = last.attribute('love')
for achievement in achievements:
if achievement.type == 'toki_love' and achievement.id == int(last.attribute('chara')):
love = str(int(achievement.data["love"]) + 1)
if achievement.data["love"] == "5":
love = "5"
self.data.local.user.put_achievement(
self.game,
self.version,
userid,
int(last.attribute('chara')),
'toki_love',
{
'love': love,
},
)
achievements = self.data.local.user.get_achievements(self.game, self.version, userid)
chara = int(last.attribute('chara'))
for achievement in achievements:
if achievement.type == 'toki_love' and achievement.id == chara:
love = int(achievement.data["love"])
if love > 5:
self.data.local.user.put_achievement(
self.game,
self.version,
userid,
chara,
'toki_love',
{
'love': str(love + 1),
},
)
break
return root
def handle_game_save_m_request(self, request: Node) -> Node:
#Score saving
# Score saving
clear_type = int(request.attribute('clear_type'))
level = int(request.attribute('level'))
@ -206,8 +199,8 @@ class HelloPopnMusic(
points = int(request.attribute('score'))
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
#pull old score
# Pull old score
oldscore = self.data.local.music.get_score(
self.game,
self.version,
@ -227,15 +220,14 @@ class HelloPopnMusic(
highscore = points >= oldscore.points
points = max(oldscore.points, points)
scoredata = oldscore.data
#Clear type
# Clear type
scoredata.replace_int('clear_type', max(scoredata.get_int('clear_type'), clear_type))
history.replace_int('clear_type', clear_type)
# Look up where this score was earned
lid = self.get_machine_id()
# Write the new score back
self.data.local.music.put_score(
self.game,
@ -261,7 +253,7 @@ class HelloPopnMusic(
history,
highscore,
)
root = Node.void('game')
return root
return root

View File

@ -3,4 +3,4 @@ from bemani.client.hellopopn.hellopopn import HelloPopnMuiscClient
__all__ = [
"HelloPopnMuiscClient",
]
]

View File

@ -1,6 +1,6 @@
import base64
import random
import time
from typing import Optional
from typing import Any, Dict, Optional
from bemani.client.base import BaseClient
from bemani.protocol import Node
@ -8,9 +8,7 @@ from bemani.protocol import Node
class HelloPopnMuiscClient(BaseClient):
NAME = ""
def verify_game_shop(self)->None:
def verify_game_shop(self) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
@ -46,8 +44,7 @@ class HelloPopnMuiscClient(BaseClient):
# Verify that response is correct
self.assert_path(resp, "response/game/@status")
def verify_game_common(self)->None:
def verify_game_common(self) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
@ -57,16 +54,21 @@ class HelloPopnMuiscClient(BaseClient):
resp = self.exchange("", call)
# Verify that response is correct
#self.__verify_profile(resp)
self.assert_path(resp, "response/game/@status")
self.assert_path(resp, "response/game/flag/@id")
self.assert_path(resp, "response/game/flag/@s1")
self.assert_path(resp, "response/game/flag/@s2")
self.assert_path(resp, "response/game/flag/@t")
self.assert_path(resp, "response/game/cnt_music")
def verify_game_new(self, loc: str, refid: str)->None:
def verify_game_new(self, loc: str, refid: str) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
game.set_attribute("locid", loc)
game.set_attribute("method", "new")
game.set_attribute("refid", refid)
game.set_attribute("ver", "0")
game.set_attribute("ver", "0")
# Swap with server
resp = self.exchange("", call)
@ -74,13 +76,13 @@ class HelloPopnMuiscClient(BaseClient):
# Verify that response is correct
self.assert_path(resp, "response/game/@status")
def verify_game_load(self, refid: str)->None:
def verify_game_load(self, refid: str) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
game.set_attribute("method", "load")
game.set_attribute("refid", refid)
game.set_attribute("ver", "0")
game.set_attribute("ver", "0")
# Swap with server
resp = self.exchange("", call)
@ -90,14 +92,14 @@ class HelloPopnMuiscClient(BaseClient):
self.assert_path(resp, "response/game/last/@level")
self.assert_path(resp, "response/game/last/@music_id")
self.assert_path(resp, "response/game/last/@style")
def verify_game_load_m(self, refid: str)->None:
def verify_game_load_m(self, refid: str) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
game.set_attribute("method", "load_m")
game.set_attribute("refid", refid)
game.set_attribute("ver", "0")
game.set_attribute("ver", "0")
# Swap with server
resp = self.exchange("", call)
@ -105,40 +107,65 @@ class HelloPopnMuiscClient(BaseClient):
# Verify that response is correct
self.assert_path(resp, "response/game/@status")
def verify_game_save_m(self, refid: str)->None:
# Grab scores
scores: Dict[int, Dict[int, int]] = {}
clears: Dict[int, Dict[int, int]] = {}
for child in resp.child("game").children:
if child.name != "music":
continue
score_data = child.child("style").child("level")
musicid = child.attribute("music_id")
chart = score_data.attribute("id")
score = score_data.attribute("score")
clear_type = score_data.attribute("clear_type")
if musicid not in scores:
scores[musicid] = {}
if musicid not in clears:
clears[musicid] = {}
scores[musicid][chart] = score
clears[musicid][chart] = clear_type
return {
"scores": scores,
"clears": clears,
}
def verify_game_save_m(self, refid: str, score: Dict[str, Any]) -> None:
call = self.call_node()
# Construct node
game = Node.void("game")
call.add_child(game)
game.set_attribute("clear_type", "2")
game.set_attribute("level", "1")
game.set_attribute("method", "save_m")
game.set_attribute("music_id", "1")
game.set_attribute("refid", refid)
game.set_attribute("score", "736000")
game.set_attribute("style", "0")
game.set_attribute("ver", "0")
game.set_attribute("clear_type", score["clear_type"])
game.set_attribute("level", score["chart"])
game.set_attribute("method", "save_m")
game.set_attribute("music_id", score["id"])
game.set_attribute("refid", refid)
game.set_attribute("score", score["score"])
game.set_attribute("style", "0")
game.set_attribute("ver", "0")
# Swap with server
resp = self.exchange("", call)
# Verify that response is correct
self.assert_path(resp, "response/game/@status")
def verify_game_save(self, loc: str , refid: str)->None:
def verify_game_save(self, loc: str , refid: str) -> None:
call = self.call_node()
game = Node.void("game")
call.add_child(game)
game.set_attribute("locid", loc)
game.set_attribute("method", "save")
game.set_attribute("refid", refid)
game.set_attribute("ver", "0")
game.set_attribute("ver", "0")
last = Node.void("last")
game.add_child(last)
last.set_attribute("chara", "1")
last.set_attribute("level", "1")
last.set_attribute("love", "1")
last.set_attribute("music_id", "1")
last.set_attribute("style", "0")
last.set_attribute("style", "0")
# Swap with server
resp = self.exchange("", call)
@ -146,7 +173,6 @@ class HelloPopnMuiscClient(BaseClient):
# Verify that response is correct
self.assert_path(resp, "response/game/@status")
def verify(self, cardid: Optional[str]) -> None:
# Verify boot sequence is okay
self.verify_services_get(
@ -181,7 +207,7 @@ class HelloPopnMuiscClient(BaseClient):
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)
self.verify_pcbevent_put()
@ -190,12 +216,12 @@ class HelloPopnMuiscClient(BaseClient):
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')
#New profile creation
self.verify_game_new(location,ref_id)
# New profile creation
self.verify_game_new(location, 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)
@ -204,8 +230,99 @@ class HelloPopnMuiscClient(BaseClient):
raise Exception(f'Invalid refid \'{ref_id}\' returned when querying card')
self.verify_game_load(ref_id)
self.verify_game_load_m(ref_id)
self.verify_game_save_m(ref_id)
self.verify_game_save(location,ref_id)
if cardid is None:
# verify score saving
for phase in [1, 2]:
if phase == 1:
dummyscores = [
# An okay score on a chart
{
"id": "1",
"chart": "1",
"clear_type": "1",
"score": "813144",
},
# A good score on an easier chart of the same song
{
"id": "1",
"chart": "0",
"clear_type": "4",
"score": "1000000",
},
# A bad score on a hard chart
{
"id": "35",
"chart": "2",
"clear_type": "1",
"score": "590523",
},
# A terrible score on an easy chart
{
"id": "28",
"chart": "0",
"clear_type": "1",
"score": "1",
},
]
# Random score to add in
songid = str(random.randint(1, 35))
chartid = str(random.randint(0, 2))
clear_type = str(random.randint(1, 4))
score = str(random.randint(0, 1000000))
dummyscores.append(
{
"id": songid,
"chart": chartid,
"clear_type": clear_type,
"score": score,
}
)
if phase == 2:
dummyscores = [
# A better score on the same chart
{
"id": "1",
"chart": "1",
"clear_type": "3",
"score": "950144",
},
# A worse score on another same chart
{
"id": "1",
"chart": "0",
"clear_type": "1",
"score": "672381",
"expected_score": "1000000",
"expected_clear": "4",
},
]
for dummyscore in dummyscores:
self.verify_game_save_m(ref_id, dummyscore)
# Sleep so we don't end up putting in score history on the same second
time.sleep(1)
scores = self.verify_game_load_m(ref_id)
for expected in dummyscores:
newscore = scores["scores"][expected["id"]][expected["chart"]]
newclear = scores["clears"][expected["id"]][expected["chart"]]
if "expected_score" in expected:
expected_score = expected["expected_score"]
else:
expected_score = expected["score"]
if "expected_clear" in expected:
expected_clear = expected["expected_clear"]
else:
expected_clear = expected["clear_type"]
if newscore != expected_score:
raise Exception(
f'Expected a score of \'{expected_score}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got score \'{newscore}\''
)
if newclear != expected_clear:
raise Exception(
f'Expected a medal of \'{expected_clear}\' for song \'{expected["id"]}\' chart \'{expected["chart"]}\' but got medal \'{newclear}\''
)
else:
print("Skipping score checks for existing card")
self.verify_game_save(location, ref_id)

View File

@ -5,4 +5,4 @@ from bemani.frontend.hellopopn.cache import HelloPopnMusicCache
__all__ = [
"HelloPopnMusicCache",
"hpnm_pages",
]
]

View File

@ -1,19 +1,12 @@
from bemani.data import Config, Data
from flask_caching import Cache
from bemani.frontend.app import app
from bemani.common import cache
from bemani.frontend.hellopopn.hellopopn import HelloPopnMusicFrontend
class HelloPopnMusicCache:
@classmethod
def preload(cls, data: Data, config: Config) -> None:
cache = Cache(
app,
config={
"CACHE_TYPE": "filesystem",
"CACHE_DIR": config.cache_dir,
},
)
frontend = HelloPopnMusicFrontend(data, config, cache)
frontend.get_all_songs(force_db_load=True)
frontend.get_all_songs(force_db_load=True)

View File

@ -1,5 +1,4 @@
import re
from flask import Blueprint
from bemani.frontend.templates import templates_location
from bemani.frontend.static import static_location
from bemani.data import UserID
@ -29,7 +28,6 @@ def viewnetworkscores() -> Response:
if len(network_scores["attempts"]) > 10:
network_scores["attempts"] = frontend.round_to_ten(network_scores["attempts"])
return render_react(
"Global Hello Pop'n Music Scores",
"hpnm/scores.react.js",
@ -47,6 +45,7 @@ def viewnetworkscores() -> Response:
},
)
@hpnm_pages.route("/scores/list")
@jsonify
@loginrequired
@ -54,6 +53,7 @@ def listnetworkscores() -> Dict[str, Any]:
frontend = HelloPopnMusicFrontend(g.data, g.config, g.cache)
return frontend.get_network_scores()
@hpnm_pages.route('/scores/<int:userid>')
@loginrequired
def viewscores(userid: UserID) -> Response:
@ -61,7 +61,7 @@ def viewscores(userid: UserID) -> Response:
info = frontend.get_latest_player_info([userid]).get(userid)
if info is None:
abort(404)
scores = frontend.get_scores(userid,limit=100)
scores = frontend.get_scores(userid, limit=100)
if len(scores) > 10:
scores = frontend.round_to_ten(scores)
@ -83,6 +83,7 @@ def viewscores(userid: UserID) -> Response:
},
)
@hpnm_pages.route("/scores/<int:userid>/list")
@jsonify
@loginrequired
@ -93,6 +94,7 @@ def listscores(userid: UserID) -> Dict[str, Any]:
"players": {},
}
@hpnm_pages.route("/topscores/<int:musicid>")
@loginrequired
def viewtopscores(musicid: int) -> Response:
@ -106,13 +108,13 @@ def viewtopscores(musicid: int) -> Response:
artist = None
for version in versions:
for chart in [0, 1, 2]:
details = g.data.local.music.get_song(GameConstants.HELLO_POPN, version, musicid, chart)
if details is not None:
if name is None:
name = details.name
if artist is None:
artist = details.artist
for chart in [0, 1, 2]:
details = g.data.local.music.get_song(GameConstants.HELLO_POPN, version, musicid, chart)
if details is not None:
if name is None:
name = details.name
if artist is None:
artist = details.artist
if name is None:
# Not a real song!
@ -161,6 +163,7 @@ def viewplayers() -> Response:
},
)
@hpnm_pages.route('/players/list')
@jsonify
@loginrequired
@ -208,6 +211,7 @@ def listplayer(userid: UserID) -> Dict[str, Any]:
"player": info,
}
@hpnm_pages.route("/options")
@loginrequired
def viewsettings() -> Response:
@ -270,6 +274,7 @@ def updatename() -> Dict[str, Any]:
"name": name,
}
@hpnm_pages.route("/records")
@loginrequired
def viewnetworkrecords() -> Response:

View File

@ -1,11 +1,11 @@
import copy
from typing import Any, Dict, Iterator, List, Tuple
from flask_caching import Cache # type: ignore
from bemani.backend.hellopopn import HelloPopnFactory,HelloPopnBase
from bemani.backend.hellopopn import HelloPopnFactory, HelloPopnBase
from bemani.common import Profile, ValidatedDict, GameConstants
from bemani.data import Data,UserID,Attempt, Song
from bemani.data import Data, UserID, Attempt, Song
from bemani.frontend.base import FrontendBase
class HelloPopnMusicFrontend(FrontendBase):
game = GameConstants.HELLO_POPN
@ -31,7 +31,7 @@ class HelloPopnMusicFrontend(FrontendBase):
formatted_profile["love"] = profile.get_str("love")
formatted_profile["level"] = profile.get_str("level")
return formatted_profile
def format_attempt(self, userid: UserID, attempt: Attempt) -> Dict[str, Any]:
formatted_attempt = super().format_attempt(userid, attempt)
formatted_attempt["clear_type"] = attempt.data.get_int("clear_type")
@ -55,4 +55,4 @@ class HelloPopnMusicFrontend(FrontendBase):
new_song = super().merge_song(existing, new)
if existing["difficulties"][new.chart] == 0:
new_song["difficulties"][new.chart] = new.data.get_int("difficulty", 1)
return new_song
return new_song

View File

@ -16,7 +16,6 @@ from bemani.common import GameConstants, cache
from bemani.data import Config, Data
def load_config(filename: str, config: Config) -> None:
config.update(yaml.safe_load(open(filename)))
config["database"]["engine"] = Data.create_engine(config)

View File

@ -4591,9 +4591,9 @@ class ImportHelloPopn(ImportBase):
'artist': song.artist,
'genre': song.genre,
'difficulty': {
'easy': 0,
'normal': 1,
'hard': 2,
'easy': 1,
'normal': 10,
'hard': 100,
},
'category': self.version
}
@ -4602,10 +4602,9 @@ class ImportHelloPopn(ImportBase):
# Reassemble the data
reassembled_songs = [val for _, val in lut.items()]
return reassembled_songs
def import_music_db(self, songs: List[Dict[str, Any]],version: int) -> None:
def import_music_db(self, songs: List[Dict[str, Any]], version: int) -> None:
if self.version is None:
raise Exception('Can\'t import database for \'all\' version!')
@ -5391,9 +5390,9 @@ def main() -> None:
'No TSV provided and no remote server specified! Please ' +
'provide either a --tsv or a --server and --token option!'
)
hellopopn.import_music_db(songs,args.version)
hellopopn.import_music_db(songs, args.version)
hellopopn.close()
else:
raise CLIException("Unsupported game series!")