4 Commits

Author SHA1 Message Date
fe8f365d8a [chunithm] fix rival music not showing up in game 2024-12-12 20:49:39 +07:00
a8f5ef1550 allnet: properly dfi encode downloadorder responses 2024-12-01 14:19:55 -05:00
383859388e chuni: fix 'NoneType' object has no attribute 'split' in score.py 2024-11-29 22:20:55 -05:00
58a5177a30 use SQL's limit/offset pagination for nextIndex/maxCount requests (#185)
Instead of retrieving the entire list of items/characters/scores/etc. at once (and even store them in memory), use SQL's `LIMIT ... OFFSET ...` pagination so we only take what we need.

Currently only CHUNITHM uses this, but this will also affect maimai DX and O.N.G.E.K.I. once the PR is ready.

Also snuck in a fix for CHUNITHM/maimai DX's `GetUserRivalMusicApi` to respect the `userRivalMusicLevelList` sent by the client.

### How this works

Say we have a `GetUserCharacterApi` request:

```json
{
    "userId": 10000,
    "maxCount": 700,
    "nextIndex": 0
}
```

Instead of getting the entire character list from the database (which can be very large if the user force unlocked everything), add limit/offset to the query:

```python
select(character)
.where(character.c.user == user_id)
.order_by(character.c.id.asc())
.limit(max_count + 1)
.offset(next_index)
```

The query takes `maxCount + 1` items from the database to determine if there is more items than can be returned:

```python
rows = ...

if len(rows) > max_count:
    # return only max_count rows
    next_index += max_count
else:
    # return everything left
    next_index = -1
```

This has the benefit of not needing to load everything into memory (and also having to store server state, as seen in the [`SCORE_BUFFER` list](2274b42358/titles/chuni/base.py (L13)).)

Reviewed-on: Hay1tsme/artemis#185
Co-authored-by: beerpsi <beerpsi@duck.com>
Co-committed-by: beerpsi <beerpsi@duck.com>
2024-11-16 19:10:29 +00:00
3 changed files with 26 additions and 5 deletions

View File

@ -349,12 +349,22 @@ class AllnetServlet:
not self.config.allnet.allow_online_updates
or not self.config.allnet.update_cfg_folder
):
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n")
resp = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n"
if is_dfi:
return PlainTextResponse(
self.to_dfi(resp) + b"\r\n", headers={ "Pragma": "DFI" }
)
return PlainTextResponse(resp)
else:
machine = await self.data.arcade.get_machine(req.serial)
if not machine or not machine['ota_enable'] or not machine['is_cab'] or machine['is_blacklisted']:
return PlainTextResponse(urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n")
resp = urllib.parse.unquote(urllib.parse.urlencode(vars(resp))) + "\n"
if is_dfi:
return PlainTextResponse(
self.to_dfi(resp) + b"\r\n", headers={ "Pragma": "DFI" }
)
return PlainTextResponse(resp)
if path.exists(
f"{self.config.allnet.update_cfg_folder}/{req.game_id}-{req.ver.replace('.', '')}-app.ini"
@ -744,7 +754,7 @@ class AllnetDownloadOrderRequest:
self.encode = req.get("encode", "")
class AllnetDownloadOrderResponse:
def __init__(self, stat: int = 1, serial: str = "", uri: str = "") -> None:
def __init__(self, stat: int = 1, serial: str = "", uri: str = "null") -> None:
self.stat = stat
self.serial = serial
self.uri = uri

View File

@ -465,7 +465,7 @@ class ChuniBase:
for d in details_iter
]
music_list.append({"musicId": music_id, "length": len(details), "userMusicDetailList": details})
music_list.append({"musicId": music_id, "length": len(details), "userRivalMusicDetailList": details})
returned_music_details_count += len(details)
if len(music_list) >= max_ct:

View File

@ -179,7 +179,14 @@ class ChuniRomVersion():
# sort it by version number for easy iteration
ChuniRomVersion.Versions = dict(sorted(all_versions.items()))
def __init__(self, rom_version: str) -> None:
def __init__(self, rom_version: Optional[str] = None) -> None:
if rom_version is None:
self.major = 0
self.minor = 0
self.maint = 0
self.version = "0.00.00"
return
(major, minor, maint) = rom_version.split('.')
self.major = int(major)
self.minor = int(minor)
@ -343,6 +350,10 @@ class ChuniScoreData(BaseData):
# for each romVersion recorded, check if it maps back the current version we are operating on
matching_rom_versions = []
for v in record_versions:
# Do this to prevent null romVersion from causing an error in ChuniRomVersion.__init__()
if v[0] is None:
continue
if ChuniRomVersion(v[0]).get_int_version() == version:
matching_rom_versions += [v[0]]