mirror of
https://github.com/DragonMinded/bemaniutils.git
synced 2026-03-21 17:24:33 -05:00
Check in initial implementation of Dance Evolution. No events or server settings yet, but basic profile works.
This commit is contained in:
parent
3435f61189
commit
51d67ca2b8
8
bemani/backend/danevo/__init__.py
Normal file
8
bemani/backend/danevo/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from bemani.backend.danevo.factory import DanceEvolutionFactory
|
||||
from bemani.backend.danevo.base import DanceEvolutionBase
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DanceEvolutionFactory",
|
||||
"DanceEvolutionBase",
|
||||
]
|
||||
23
bemani/backend/danevo/base.py
Normal file
23
bemani/backend/danevo/base.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# vim: set fileencoding=utf-8
|
||||
from typing import Optional
|
||||
|
||||
from bemani.backend.base import Base
|
||||
from bemani.backend.core import CoreHandler, CardManagerHandler, PASELIHandler
|
||||
from bemani.common import (
|
||||
GameConstants,
|
||||
)
|
||||
|
||||
|
||||
class DanceEvolutionBase(CoreHandler, CardManagerHandler, PASELIHandler, Base):
|
||||
"""
|
||||
Base game class for all Dance Evolution version that we support.
|
||||
"""
|
||||
|
||||
game: GameConstants = GameConstants.DANCE_EVOLUTION
|
||||
|
||||
def previous_version(self) -> Optional["DanceEvolutionBase"]:
|
||||
"""
|
||||
Returns the previous version of the game, based on this game. Should
|
||||
be overridden.
|
||||
"""
|
||||
return None
|
||||
146
bemani/backend/danevo/danevo.py
Normal file
146
bemani/backend/danevo/danevo.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import base64
|
||||
from typing import Any, Dict
|
||||
|
||||
from bemani.backend.ess import EventLogHandler
|
||||
from bemani.backend.danevo.base import DanceEvolutionBase
|
||||
from bemani.common import VersionConstants, Profile, CardCipher, Time
|
||||
from bemani.protocol import Node
|
||||
|
||||
|
||||
class DanceEvolution(
|
||||
EventLogHandler,
|
||||
DanceEvolutionBase,
|
||||
):
|
||||
name: str = "Dance Evolution"
|
||||
version: int = VersionConstants.DANCE_EVOLUTION
|
||||
|
||||
@classmethod
|
||||
def get_settings(cls) -> Dict[str, Any]:
|
||||
"""
|
||||
Return all of our front-end modifiably settings.
|
||||
"""
|
||||
return {}
|
||||
|
||||
def handle_tax_get_phase_request(self, request: Node) -> Node:
|
||||
tax = Node.void("tax")
|
||||
tax.add_child(Node.s32("phase", 0))
|
||||
return tax
|
||||
|
||||
def handle_system_convcardnumber_request(self, request: Node) -> Node:
|
||||
cardid = request.child_value("data/card_id")
|
||||
cardnumber = CardCipher.encode(cardid)
|
||||
|
||||
system = Node.void("system")
|
||||
data = Node.void("data")
|
||||
system.add_child(data)
|
||||
|
||||
system.add_child(Node.s32("result", 0))
|
||||
data.add_child(Node.string("card_number", cardnumber))
|
||||
return system
|
||||
|
||||
def handle_system_getmaster_request(self, request: Node) -> Node:
|
||||
# See if we can grab the request
|
||||
data = request.child("data")
|
||||
if not data:
|
||||
root = Node.void("system")
|
||||
root.add_child(Node.s32("result", 0))
|
||||
return root
|
||||
|
||||
# Figure out what type of messsage this is
|
||||
reqtype = data.child_value("datatype")
|
||||
reqkey = data.child_value("datakey") # noqa
|
||||
|
||||
# System message
|
||||
root = Node.void("system")
|
||||
|
||||
if reqtype == "S_SRVMSG":
|
||||
# Known keys include: INFO, ARK_ARR0, ARK_HAS0, SONGOPEN, IRDATA, EVTMSG3, WEEKLYSO
|
||||
root.add_child(Node.string("strdata1", ""))
|
||||
root.add_child(Node.string("strdata2", ""))
|
||||
root.add_child(Node.u64("updatedate", Time.now() * 1000))
|
||||
root.add_child(Node.s32("result", 1))
|
||||
else:
|
||||
# Unknown message.
|
||||
root.add_child(Node.s32("result", 0))
|
||||
|
||||
return root
|
||||
|
||||
def handle_playerdata_usergamedata_recv_request(self, request: Node) -> Node:
|
||||
playerdata = Node.void("playerdata")
|
||||
|
||||
player = Node.void("player")
|
||||
playerdata.add_child(player)
|
||||
|
||||
refid = request.child_value("data/refid")
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid)
|
||||
records = 0
|
||||
|
||||
record = Node.void("record")
|
||||
player.add_child(record)
|
||||
|
||||
def danevohex(val: int) -> str:
|
||||
return hex(val)[2:]
|
||||
|
||||
if profile is None:
|
||||
# Just return a default empty node
|
||||
record.add_child(Node.string("d", "<NODATA>"))
|
||||
records = 1
|
||||
else:
|
||||
# Figure out what profiles are being requested
|
||||
profiletypes = request.child_value("data/recv_csv").split(",")[::2]
|
||||
usergamedata = profile.get_dict("usergamedata")
|
||||
for ptype in profiletypes:
|
||||
if ptype in usergamedata:
|
||||
records = records + 1
|
||||
|
||||
dnode = Node.string(
|
||||
"d",
|
||||
base64.b64encode(usergamedata[ptype]["strdata"]).decode("ascii"),
|
||||
)
|
||||
dnode.add_child(
|
||||
Node.string(
|
||||
"bin1",
|
||||
base64.b64encode(usergamedata[ptype]["bindata"]).decode("ascii"),
|
||||
)
|
||||
)
|
||||
record.add_child(dnode)
|
||||
|
||||
player.add_child(Node.u32("record_num", records))
|
||||
|
||||
playerdata.add_child(Node.s32("result", 0))
|
||||
return playerdata
|
||||
|
||||
def handle_playerdata_usergamedata_send_request(self, request: Node) -> Node:
|
||||
playerdata = Node.void("playerdata")
|
||||
refid = request.child_value("data/refid")
|
||||
|
||||
userid = self.data.remote.user.from_refid(self.game, self.version, refid)
|
||||
if userid is not None:
|
||||
profile = self.get_profile(userid) or Profile(self.game, self.version, refid, 0)
|
||||
usergamedata = profile.get_dict("usergamedata")
|
||||
|
||||
for record in request.child("data/record").children:
|
||||
if record.name != "d":
|
||||
continue
|
||||
|
||||
strdata = base64.b64decode(record.value)
|
||||
bindata = base64.b64decode(record.child_value("bin1"))
|
||||
|
||||
# Grab and format the profile objects
|
||||
strdatalist = strdata.split(b",")
|
||||
profiletype = strdatalist[1].decode("utf-8")
|
||||
strdatalist = strdatalist[2:]
|
||||
|
||||
usergamedata[profiletype] = {
|
||||
"strdata": b",".join(strdatalist),
|
||||
"bindata": bindata,
|
||||
}
|
||||
|
||||
profile.replace_dict("usergamedata", usergamedata)
|
||||
profile.replace_int("write_time", Time.now())
|
||||
self.put_profile(userid, profile)
|
||||
|
||||
playerdata.add_child(Node.s32("result", 0))
|
||||
return playerdata
|
||||
28
bemani/backend/danevo/factory.py
Normal file
28
bemani/backend/danevo/factory.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from typing import List, Optional, Type
|
||||
|
||||
from bemani.backend.base import Base, Factory
|
||||
from bemani.backend.danevo.danevo import DanceEvolution
|
||||
from bemani.common import Model
|
||||
from bemani.data import Config, Data
|
||||
|
||||
|
||||
class DanceEvolutionFactory(Factory):
|
||||
MANAGED_CLASSES: List[Type[Base]] = [
|
||||
DanceEvolution,
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def register_all(cls) -> None:
|
||||
for gamecode in ["KDM"]:
|
||||
Base.register(gamecode, DanceEvolutionFactory)
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
data: Data,
|
||||
config: Config,
|
||||
model: Model,
|
||||
parentmodel: Optional[Model] = None,
|
||||
) -> Optional[Base]:
|
||||
# There is only one Dance Evolution.
|
||||
return DanceEvolution(data, config, model)
|
||||
|
|
@ -32,6 +32,8 @@ class VersionConstants:
|
|||
|
||||
BISHI_BASHI_TSBB: Final[int] = 1
|
||||
|
||||
DANCE_EVOLUTION: Final[int] = 1
|
||||
|
||||
DDR_1STMIX: Final[int] = 1
|
||||
DDR_2NDMIX: Final[int] = 2
|
||||
DDR_3RDMIX: Final[int] = 3
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from bemani.backend.iidx import IIDXFactory
|
|||
from bemani.backend.popn import PopnMusicFactory
|
||||
from bemani.backend.jubeat import JubeatFactory
|
||||
from bemani.backend.bishi import BishiBashiFactory
|
||||
from bemani.backend.danevo import DanceEvolutionFactory
|
||||
from bemani.backend.ddr import DDRFactory
|
||||
from bemani.backend.sdvx import SoundVoltexFactory
|
||||
from bemani.backend.reflec import ReflecBeatFactory
|
||||
|
|
@ -78,3 +79,5 @@ def register_games(config: Config) -> None:
|
|||
MusecaFactory.register_all()
|
||||
if GameConstants.MGA in config.support:
|
||||
MetalGearArcadeFactory.register_all()
|
||||
if GameConstants.DANCE_EVOLUTION in config.support:
|
||||
DanceEvolutionFactory.register_all()
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ def register_blueprints() -> None:
|
|||
app.register_blueprint(reflec_pages)
|
||||
if GameConstants.MUSECA in config.support:
|
||||
app.register_blueprint(museca_pages)
|
||||
# TODO: DanEvo frontend here.
|
||||
|
||||
|
||||
def register_games() -> None:
|
||||
|
|
|
|||
|
|
@ -6106,28 +6106,46 @@ class ImportDanceEvolution(ImportBase):
|
|||
for i in range(numsongs):
|
||||
offset = (i * 128) + 16
|
||||
|
||||
songcode = get_string(offset + 0) # noqa: F841
|
||||
songcode = get_string(offset + 0)
|
||||
songres1 = get_string(offset + 4) # noqa: F841
|
||||
songres2 = get_string(offset + 8) # noqa: F841
|
||||
bpm_min = get_int(offset + 12)
|
||||
bpm_max = get_int(offset + 16)
|
||||
|
||||
# Unknown 4 byte value at offset 20.
|
||||
|
||||
copyright = get_string(offset + 24, "")
|
||||
|
||||
# Unknown 4 byte value at offset 28, 36, 40, 44, 48.
|
||||
|
||||
title = get_string(offset + 52, "Unknown song")
|
||||
artist = get_string(offset + 56, "Unknown artist")
|
||||
|
||||
# Unknown 4 byte value at offset 60.
|
||||
|
||||
level = get_int(offset + 64)
|
||||
|
||||
# Unknown 4 byte value at offset 68.
|
||||
|
||||
charares1 = get_string(offset + 72) # noqa: F841
|
||||
charares2 = get_string(offset + 76) # noqa: F841
|
||||
|
||||
# Unknown 4 byte values at offset 80, 84, 88, 92, 96, 100, 104.
|
||||
|
||||
kana_sort = get_string(offset + 108)
|
||||
|
||||
flag1 = data[offset + 33] != 0x00 # noqa: F841
|
||||
flag2 = data[offset + 34] == 0x01 # noqa: F841
|
||||
flag3 = data[offset + 34] == 0x02 # noqa: F841
|
||||
flag4 = data[offset + 116] != 0x00 # noqa: F841
|
||||
# Unknown 4 byte value at offset 112.
|
||||
|
||||
flag1 = data[offset + 33] != 0x00
|
||||
flag2 = data[offset + 34] == 0x01
|
||||
flag3 = data[offset + 34] == 0x02
|
||||
flag4 = data[offset + 35] != 0x00
|
||||
|
||||
# TODO: Get the real music ID from the data, once we have in-game traffic.
|
||||
retval.append(
|
||||
{
|
||||
"id": i,
|
||||
"code": songcode,
|
||||
"title": title,
|
||||
"artist": artist,
|
||||
"copyright": copyright or None,
|
||||
|
|
@ -6135,6 +6153,10 @@ class ImportDanceEvolution(ImportBase):
|
|||
"bpm_min": bpm_min,
|
||||
"bpm_max": bpm_max,
|
||||
"level": level,
|
||||
"flag1": flag1,
|
||||
"flag2": flag2,
|
||||
"flag3": flag3,
|
||||
"flag4": flag4,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from bemani.backend.ddr import DDRFactory
|
|||
from bemani.backend.sdvx import SoundVoltexFactory
|
||||
from bemani.backend.reflec import ReflecBeatFactory
|
||||
from bemani.backend.museca import MusecaFactory
|
||||
from bemani.backend.danevo import DanceEvolutionFactory
|
||||
from bemani.frontend.popn import PopnMusicCache
|
||||
from bemani.frontend.iidx import IIDXCache
|
||||
from bemani.frontend.jubeat import JubeatCache
|
||||
|
|
@ -57,6 +58,9 @@ def run_scheduled_work(config: Config) -> None:
|
|||
if GameConstants.MUSECA in config.support:
|
||||
enabled_factories.append(MusecaFactory)
|
||||
enabled_caches.append(MusecaCache)
|
||||
if GameConstants.DANCE_EVOLUTION in config.support:
|
||||
enabled_factories.append(DanceEvolutionFactory)
|
||||
# TODO: Frontend cache here.
|
||||
|
||||
# First, run any backend scheduled work
|
||||
for factory in enabled_factories:
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ def get_client(proto: ClientProtocol, pcbid: str, game: str, config: Dict[str, A
|
|||
pcbid,
|
||||
config,
|
||||
)
|
||||
# TODO: DanEvo client here.
|
||||
|
||||
raise Exception(f"Unknown game {game}")
|
||||
|
||||
|
|
|
|||
|
|
@ -71,14 +71,16 @@ paseli:
|
|||
support:
|
||||
# Bishi Bashi frontend/backend enabled.
|
||||
bishi: True
|
||||
# Metal Gear Arcade frontend/backend enabled
|
||||
mga: True
|
||||
# Dance Evolution frontend/backend enable.
|
||||
danevo: True
|
||||
# DDR frontend/backend enabled
|
||||
ddr: True
|
||||
# IIDX frontend/backend enabled.
|
||||
iidx: True
|
||||
# Jubeat frontend/backend enabled.
|
||||
jubeat: True
|
||||
# Metal Gear Arcade frontend/backend enabled
|
||||
mga: True
|
||||
# Museca frontend/backend enabled.
|
||||
museca: True
|
||||
# Pop'n Music frontend/backend enabled.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user