[Enhance] Link Play Rooms info & API for that

- Add an HTTP API endpoint for getting the information of rooms and players in Link Play
This commit is contained in:
Lost-MSth
2023-12-03 16:42:53 +08:00
parent 150686d9f8
commit f3c17cdde7
5 changed files with 129 additions and 40 deletions

View File

@@ -1,15 +1,10 @@
from flask import Blueprint
from . import (users, songs, token, system, items,
purchases, presents, redeems, characters)
purchases, presents, redeems, characters, multiplay)
bp = Blueprint('api', __name__, url_prefix='/api/v1')
bp.register_blueprint(users.bp)
bp.register_blueprint(songs.bp)
bp.register_blueprint(token.bp)
bp.register_blueprint(system.bp)
bp.register_blueprint(items.bp)
bp.register_blueprint(purchases.bp)
bp.register_blueprint(presents.bp)
bp.register_blueprint(redeems.bp)
bp.register_blueprint(characters.bp)
l = [users, songs, token, system, items, purchases,
presents, redeems, characters, multiplay]
for i in l:
bp.register_blueprint(i.bp)

View File

@@ -0,0 +1,21 @@
from flask import Blueprint, request
from core.linkplay import RemoteMultiPlayer
from .api_auth import api_try, request_json_handle, role_required
from .api_code import success_return
bp = Blueprint('multiplay', __name__, url_prefix='/multiplay')
@bp.route('/rooms', methods=['GET'])
@role_required(request, ['select'])
@request_json_handle(request, optional_keys=['offset', 'limit'])
@api_try
def rooms_get(data, user):
'''获取房间列表'''
r = RemoteMultiPlayer().get_rooms(offset=data.get(
'offset', 0), limit=data.get('limit', 100))
return success_return(r)

View File

@@ -138,35 +138,6 @@ class RemoteMultiPlayer:
return self.data_recv
# if self.data_recv[0] != '0':
# code = int(self.data_recv[0])
# raise ArcError(f'Link Play error code: {code}', code, status=400)
# @staticmethod
# def tcp(data: str) -> str:
# with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
# sock.connect((Constant.LINKPLAY_HOST,
# Constant.LINKPLAY_TCP_PORT))
# send_data =
# sock.sendall(bytes(data + "\n", "utf-8"))
# try:
# received = str(sock.recv(1024), "utf-8").strip()
# except socket.timeout as e:
# raise Timeout(
# 'Timeout when waiting for data from link play server.', status=400) from e
# # print(received)
# return received
# def data_swap(self, data: tuple) -> tuple:
# received = self.tcp(Constant.LINKPLAY_AUTHENTICATION +
# '|' + '|'.join([str(x) for x in data]))
# self.data_recv = received.split('|')
# if self.data_recv[0] != '0':
# code = int(self.data_recv[0])
# raise ArcError(f'Link Play error code: {code}', code, status=400)
def create_room(self, user: 'Player' = None) -> None:
'''创建房间'''
if user is not None:
@@ -231,3 +202,15 @@ class RemoteMultiPlayer:
self.room.song_unlock = b64decode(x['song_unlock'])
self.user.key = b64decode(x['key'])
self.user.player_id = int(x['player_id'])
def get_rooms(self, offset=0, limit=50) -> dict:
'''获取房间列表'''
self.data_swap({
'endpoint': 'get_rooms',
'data': {
'offset': offset,
'limit': limit
}
})
return self.data_recv['data']

View File

@@ -91,6 +91,7 @@ class TCPRouter:
'create_room',
'join_room',
'update_room',
'get_rooms',
}
def __init__(self, raw_data: 'dict | list'):
@@ -115,7 +116,11 @@ class TCPRouter:
self.clean_check()
if self.endpoint not in self.router:
return None
r = getattr(self, self.endpoint)()
try:
r = getattr(self, self.endpoint)()
except Exception as e:
logging.error(e)
return 999
if isinstance(r, int):
return {'code': r}
return {
@@ -257,3 +262,41 @@ class TCPRouter:
'player_id': r['player_id'],
'song_unlock': b64encode(room.song_unlock).decode('utf-8')
}
def get_rooms(self) -> dict:
# 获取房间列表与详细信息
offset = int(self.data.get('offset', 0))
if offset < 0:
offset = 0
limit = min(int(self.data.get('limit', 100)), 100)
if limit < 0:
limit = 100
n = 0
m = 0
rooms = []
f = False
f2 = False
for room in Store.room_id_dict.values():
if room.player_num == 0:
continue
if m < offset:
m += 1
continue
if f:
# 处理刚好有 limit 个房间的情况
f2 = True
break
n += 1
rooms.append(room.to_dict())
if n >= limit:
f = True
return {
'amount': n,
'offset': offset,
'limit': limit,
'has_more': f2,
'rooms': rooms
}

View File

@@ -49,6 +49,27 @@ class Player:
def name(self) -> str:
return self.player_name.decode('ascii').rstrip('\x00')
def to_dict(self) -> dict:
return {
'multiplay_player_id': self.player_id,
'name': self.name,
'is_online': self.online == 1,
'character_id': self.character_id,
'is_uncapped': self.is_uncapped == 1,
'last_song': {
'difficulty': self.last_difficulty,
'score': self.last_score,
'cleartype': self.last_cleartype,
},
'song': {
'difficulty': self.difficulty,
'score': self.score,
'cleartype': self.cleartype,
},
'player_state': self.player_state,
'last_timestamp': self.last_timestamp,
}
def set_player_name(self, player_name: str):
self.player_name = player_name.encode('ascii')
if len(self.player_name) > 16:
@@ -80,6 +101,32 @@ class Room:
self.command_queue = []
def to_dict(self) -> dict:
p = [i.to_dict() for i in self.players if i.player_id != 0]
for i in p:
i['is_host'] = i['player_id'] == self.host_id
return {
'room_id': self.room_id,
'room_code': self.room_code,
'state': self.state,
'song_idx': self.song_idx,
'last_song_idx': self.last_song_idx if not self.is_playing else 0xffff,
'host_id': self.host_id,
'players': p,
'round_switch': self.round_switch == 1,
'last_timestamp': self.timestamp,
'is_enterable': self.is_enterable,
'is_playing': self.is_playing,
}
@property
def is_enterable(self) -> bool:
return 0 < self.player_num < 4 and self.state == 2
@property
def is_playing(self) -> bool:
return self.state in (4, 5, 6, 7)
@property
def command_queue_length(self) -> int:
return len(self.command_queue)