Files
Arcaea-server/latest version/linkplay_server/store.py
Lost-MSth 1672d337ff [Enhance] [Refactor] Add API about characters
- Add API endpoints about characters
- Some small changes in refactoring
2023-03-22 22:27:21 +08:00

232 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
from base64 import b64decode, b64encode
from os import urandom
from random import randint
from threading import RLock
from time import time
from .config import Config
from .udp_class import Player, Room, bi
class Store:
# token: {'key': key, 'room': Room, 'player_index': player_index, 'player_id': player_id}
link_play_data = {}
room_id_dict = {} # 'room_id': Room
room_code_dict = {} # 'room_code': Room
player_dict = {} # 'player_id' : Player
lock = RLock()
def random_room_code():
re = ''
for _ in range(4):
re += chr(randint(65, 90))
for _ in range(2):
re += str(randint(0, 9))
return re
def unique_random(dataset, length=8, random_func=None):
'''无重复随机且默认非0没处理可能的死循环'''
if random_func is None:
x = bi(urandom(length))
while x in dataset or x == 0:
x = bi(urandom(length))
else:
x = random_func()
while x in dataset:
x = random_func()
return x
def clear_player(token):
# 清除玩家信息和token
del Store.player_dict[Store.link_play_data[token]['player_id']]
del Store.link_play_data[token]
def clear_room(room):
# 清除房间信息
room_id = room.room_id
room_code = room.room_code
del Store.room_id_dict[room_id]
del Store.room_code_dict[room_code]
del room
def memory_clean(now):
# 内存清理,应对玩家不正常退出
with Store.lock:
clean_room_list = []
clean_player_list = []
for token, v in Store.link_play_data.items():
room = v['room']
if now - room.timestamp >= Config.TIME_LIMIT:
clean_room_list.append(room.room_id)
if now - room.players[v['player_index']].last_timestamp // 1000 >= Config.TIME_LIMIT:
clean_player_list.append(token)
for room_id, v in Store.room_id_dict.items():
if now - v.timestamp >= Config.TIME_LIMIT:
clean_room_list.append(room_id)
for room_id in clean_room_list:
if room_id in Store.room_id_dict:
clear_room(Store.room_id_dict[room_id])
for token in clean_player_list:
clear_player(token)
class TCPRouter:
clean_timer = 0
router = {
'0': 'debug',
'1': 'create_room',
'2': 'join_room',
'3': 'update_room',
}
def __init__(self, data: list):
self.data = data # data: list[str] = [command, ...]
def debug(self):
if Config.DEBUG:
return eval(self.data[1])
return 'ok'
@staticmethod
def clean_check():
now = round(time() * 1000)
if now - TCPRouter.clean_timer >= Config.TIME_LIMIT:
logging.info('Start cleaning memory...')
TCPRouter.clean_timer = now
memory_clean(now)
def handle(self) -> str:
self.clean_check()
if self.data[0] not in self.router:
return None
r = getattr(self, self.router[self.data[0]])()
if isinstance(r, tuple):
return '|'.join(map(str, r))
return str(r)
@staticmethod
def generate_player(name: str) -> Player:
player_id = unique_random(Store.player_dict, 3)
player = Player()
player.player_id = player_id
player.set_player_name(name)
Store.player_dict[player_id] = player
return player
@staticmethod
def generate_room() -> Room:
room_id = unique_random(Store.room_id_dict)
room = Room()
room.room_id = room_id
room.timestamp = round(time() * 1000)
Store.room_id_dict[room_id] = room
room_code = unique_random(
Store.room_code_dict, random_func=random_room_code)
room.room_code = room_code
Store.room_code_dict[room_code] = room
return room
def create_room(self) -> tuple:
# 开房
# data = ['1', name, song_unlock, ]
# song_unlock: base64 str
name = self.data[1]
song_unlock = b64decode(self.data[2])
key = urandom(16)
with Store.lock:
room = self.generate_room()
player = self.generate_player(name)
player.song_unlock = song_unlock
room.song_unlock = song_unlock
room.host_id = player.player_id
room.players[0] = player
token = room.room_id
player.token = token
Store.link_play_data[token] = {
'key': key,
'room': room,
'player_index': 0,
'player_id': player.player_id
}
logging.info(f'TCP-Create room `{room.room_code}` by player `{name}`')
return (0, room.room_code, room.room_id, token, b64encode(key).decode('utf-8'), player.player_id)
def join_room(self) -> tuple:
# 入房
# data = ['2', name, song_unlock, room_code]
# song_unlock: base64 str
room_code = self.data[3].upper()
key = urandom(16)
name = self.data[1]
song_unlock = b64decode(self.data[2])
with Store.lock:
if room_code not in Store.room_code_dict:
# 房间号错误 / 房间不存在
return '1202'
room: Room = Store.room_code_dict[room_code]
player_num = room.player_num
if player_num == 4:
# 满人
return '1201'
if player_num == 0:
# 房间不存在
return '1202'
if room.state != 2:
# 无法加入
return '1205'
token = unique_random(Store.link_play_data)
player = self.generate_player(name)
player.token = token
player.song_unlock = song_unlock
room.update_song_unlock()
for i in range(4):
if room.players[i].player_id == 0:
room.players[i] = player
player_index = i
break
Store.link_play_data[token] = {
'key': key,
'room': room,
'player_index': player_index,
'player_id': player.player_id
}
logging.info(f'TCP-Player `{name}` joins room `{room_code}`')
return (0, room_code, room.room_id, token, b64encode(key).decode('utf-8'), player.player_id, b64encode(room.song_unlock).decode('utf-8'))
def update_room(self) -> tuple:
# 房间信息更新
# data = ['3', token]
token = int(self.data[1])
with Store.lock:
if token not in Store.link_play_data:
return '108'
r = Store.link_play_data[token]
room = r['room']
logging.info(f'TCP-Room `{room.room_code}` info update')
return (0, room.room_code, room.room_id, b64encode(r['key']).decode('utf-8'), room.players[r['player_index']].player_id, b64encode(room.song_unlock).decode('utf-8'))