[Enhance] Link Play 2.0 e.t.c.

- For Arcaea 5.10.1(c)
- Add support for Link Play 2.0.
- New partners "Luna & Ilot" and "Eto & Hoppe"
- Add support for the skill of "Eto & Hoppe".
- Add support for refreshing ratings of Recent 30 via API and webpage.

Note: This is a bug testing version.
This commit is contained in:
Lost-MSth
2024-09-06 22:43:38 +08:00
parent 59422f96b5
commit 014531f3f1
21 changed files with 1184 additions and 253 deletions

View File

@@ -1,28 +1,43 @@
from os import urandom
from time import time
from .udp_class import Room, b
PADDING = [b(i) * i for i in range(16)] + [b'']
class CommandSender:
PROTOCOL_NAME = b'\x06\x16'
PROTOCOL_VERSION = b'\x09'
PROTOCOL_VERSION = b'\x0D'
def __init__(self, room: Room = None) -> None:
self.room = room
self.timestamp = round(time() * 1000000)
self.room.timestamp = self.timestamp + 1
self.random_code = b'\x11\x11\x11\x11\x00\x00\x00\x00'
self._random_code = None
@property
def random_code(self):
if self._random_code is None:
self._random_code = urandom(4) + b'\x00\x00\x00\x00'
return self._random_code
@random_code.setter
def random_code(self, value):
self._random_code = value
@staticmethod
def command_encode(t: tuple):
r = b''.join(t)
x = 16 - len(r) % 16
return r + b(x) * x
return r + PADDING[x]
def command_prefix(self, command: bytes):
length = self.room.command_queue_length
if command >= b'\x10':
if b'\x10' <= command <= b'\x1f':
length += 1
return (self.PROTOCOL_NAME, command, self.PROTOCOL_VERSION, b(self.room.room_id, 8), b(length, 4))
@@ -31,12 +46,18 @@ class CommandSender:
return self.command_encode((*self.command_prefix(b'\x0c'), self.random_code, b(self.room.state), b(self.room.countdown, 4), b(self.timestamp, 8)))
def command_0d(self, code: int):
# 3 你不是房主
# 5 有玩家目前无法开始
# 6 需要更多玩家以开始
# 7 有玩家无法游玩这首歌
return self.command_encode((*self.command_prefix(b'\x0d'), self.random_code, b(code)))
def command_0e(self, player_index: int):
# 分数广播
# 我猜616 写错了,首先 4 个 00 大概是分数使用了 8 bytes 转换,其次上一个分数根本就不需要哈哈哈哈哈哈!
player = self.room.players[player_index]
return self.command_encode((*self.command_prefix(b'\x0e'), b(player.player_id, 8), b(player.character_id), b(player.is_uncapped), b(player.difficulty), b(player.score, 4), b(player.timer, 4), b(player.cleartype), b(player.player_state), b(player.download_percent), b'\x01', b(player.last_score, 4), b(player.last_timer, 4), b(player.online)))
return self.command_encode((*self.command_prefix(b'\x0e'), player.info, b(player.last_score.score, 4), b'\x00' * 4, b(player.last_score.timer, 4), b'\x00' * 4))
def command_0f(self, player_index: int, song_idx: int):
# 歌曲推荐
@@ -52,13 +73,17 @@ class CommandSender:
def command_12(self, player_index: int):
player = self.room.players[player_index]
return self.command_encode((*self.command_prefix(b'\x12'), self.random_code, b(player_index), b(player.player_id, 8), b(player.character_id), b(player.is_uncapped), b(player.difficulty), b(player.score, 4), b(player.timer, 4), b(player.cleartype), b(player.player_state), b(player.download_percent), b(player.online)))
return self.command_encode((*self.command_prefix(b'\x12'), self.random_code, b(player_index), player.info))
def command_13(self):
return self.command_encode((*self.command_prefix(b'\x13'), self.random_code, b(self.room.host_id, 8), b(self.room.state), b(self.room.countdown, 4), b(self.timestamp, 8), b(self.room.song_idx, 2), b(self.room.interval, 2), b(self.room.times, 7), self.room.get_player_last_score(), b(self.room.last_song_idx, 2), b(self.room.round_switch, 1)))
return self.command_encode((*self.command_prefix(b'\x13'), self.random_code, self.room.room_info))
def command_14(self):
return self.command_encode((*self.command_prefix(b'\x14'), self.random_code, self.room.song_unlock))
def command_15(self):
return self.command_encode((*self.command_prefix(b'\x15'), self.room.get_players_info(), self.room.song_unlock, b(self.room.host_id, 8), b(self.room.state), b(self.room.countdown, 4), b(self.timestamp, 8), b(self.room.song_idx, 2), b(self.room.interval, 2), b(self.room.times, 7), self.room.get_player_last_score(), b(self.room.last_song_idx, 2), b(self.room.round_switch, 1)))
return self.command_encode((*self.command_prefix(b'\x15'), self.room.get_players_info(), self.room.song_unlock, self.room.room_info))
def command_21(self, player_index: int, sticker_id: int):
player = self.room.players[player_index]
return self.command_encode((*self.command_prefix(b'\x21'), b(player.player_id, 8), b(sticker_id, 2)))