mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2026-02-09 01:07:27 +08:00
Rearrange Link Play module
- Try to ensure thread safety by using TCP socket
This commit is contained in:
1
latest version/linkplay_server/__init__.py
Normal file
1
latest version/linkplay_server/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .main import link_play
|
||||
24
latest version/linkplay_server/aes.py
Normal file
24
latest version/linkplay_server/aes.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from os import urandom
|
||||
from cryptography.hazmat.primitives.ciphers import (
|
||||
Cipher, algorithms, modes
|
||||
)
|
||||
|
||||
|
||||
def encrypt(key, plaintext, associated_data):
|
||||
iv = urandom(12)
|
||||
encryptor = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.GCM(iv, min_tag_length=12),
|
||||
).encryptor()
|
||||
encryptor.authenticate_additional_data(associated_data)
|
||||
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
|
||||
return (iv, ciphertext, encryptor.tag)
|
||||
|
||||
|
||||
def decrypt(key, associated_data, iv, ciphertext, tag):
|
||||
decryptor = Cipher(
|
||||
algorithms.AES(key),
|
||||
modes.GCM(iv, tag, min_tag_length=12),
|
||||
).decryptor()
|
||||
decryptor.authenticate_additional_data(associated_data)
|
||||
return decryptor.update(ciphertext) + decryptor.finalize()
|
||||
30
latest version/linkplay_server/config.py
Normal file
30
latest version/linkplay_server/config.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from telnetlib import AUTHENTICATION
|
||||
|
||||
|
||||
class Config:
|
||||
'''
|
||||
Link Play server configuration
|
||||
'''
|
||||
|
||||
'''
|
||||
服务器地址、端口号、校验码
|
||||
Server address, port and verification code
|
||||
'''
|
||||
HOST = '0.0.0.0'
|
||||
UDP_PORT = 10900
|
||||
TCP_PORT = 10901
|
||||
AUTHENTICATION = 'my_link_play_server'
|
||||
'''
|
||||
--------------------------------------------------
|
||||
'''
|
||||
|
||||
TIME_LIMIT = 3600000
|
||||
|
||||
COMMAND_INTERVAL = 1000000
|
||||
|
||||
COUNTDOWM_TIME = 3999
|
||||
|
||||
PLAYER_PRE_TIMEOUT = 3000000
|
||||
PLAYER_TIMEOUT = 20000000
|
||||
|
||||
LINK_PLAY_UNLOCK_LENGTH = 512
|
||||
253
latest version/linkplay_server/main.py
Normal file
253
latest version/linkplay_server/main.py
Normal file
@@ -0,0 +1,253 @@
|
||||
import base64
|
||||
import random
|
||||
import socketserver
|
||||
import threading
|
||||
import time
|
||||
from os import urandom
|
||||
|
||||
# import binascii
|
||||
from .aes import decrypt, encrypt
|
||||
from .config import Config
|
||||
from .udp_class import Player, Room, bi
|
||||
from .udp_parser import CommandParser
|
||||
|
||||
# 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
|
||||
clean_timer = 0
|
||||
lock = threading.RLock()
|
||||
|
||||
|
||||
def random_room_code():
|
||||
# 随机生成房间号
|
||||
re = ''
|
||||
for _ in range(4):
|
||||
re += chr(random.randint(65, 90))
|
||||
for _ in range(2):
|
||||
re += str(random.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 player_dict[link_play_data[token]['player_id']]
|
||||
del link_play_data[token]
|
||||
|
||||
|
||||
def clear_room(room):
|
||||
# 清除房间信息
|
||||
room_id = room.room_id
|
||||
room_code = room.room_code
|
||||
del room_id_dict[room_id]
|
||||
del room_code_dict[room_code]
|
||||
del room
|
||||
|
||||
|
||||
def memory_clean(now):
|
||||
# 内存清理
|
||||
lock.acquire()
|
||||
|
||||
clean_room_list = []
|
||||
clean_player_list = []
|
||||
for token in link_play_data:
|
||||
room = link_play_data[token]['room']
|
||||
if now - room.timestamp >= Config.TIME_LIMIT:
|
||||
clean_room_list.append(room.room_id)
|
||||
|
||||
if now - room.players[link_play_data[token]['player_index']].last_timestamp // 1000 >= Config.TIME_LIMIT:
|
||||
clean_player_list.append(token)
|
||||
|
||||
for room_id in room_id_dict:
|
||||
if now - room_id_dict[room_id].timestamp >= Config.TIME_LIMIT:
|
||||
clean_room_list.append(room_id)
|
||||
|
||||
for room_id in clean_room_list:
|
||||
if room_id in room_id_dict:
|
||||
clear_room(room_id_dict[room_id])
|
||||
|
||||
for token in clean_player_list:
|
||||
clear_player(token)
|
||||
|
||||
lock.release()
|
||||
|
||||
|
||||
class UDP_handler(socketserver.BaseRequestHandler):
|
||||
def handle(self):
|
||||
client_msg, server = self.request
|
||||
try:
|
||||
token = client_msg[:8]
|
||||
iv = client_msg[8:20]
|
||||
tag = client_msg[20:32]
|
||||
ciphertext = client_msg[32:]
|
||||
if int.from_bytes(token, byteorder='little') in link_play_data:
|
||||
user = link_play_data[bi(token)]
|
||||
else:
|
||||
return None
|
||||
|
||||
plaintext = decrypt(user['key'], b'', iv, ciphertext, tag)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return None
|
||||
# print(binascii.b2a_hex(plaintext))
|
||||
|
||||
commands = CommandParser(
|
||||
user['room'], user['player_index']).get_commands(plaintext)
|
||||
|
||||
if user['room'].players[user['player_index']].player_id == 0:
|
||||
clear_player(bi(token))
|
||||
temp = []
|
||||
for i in commands:
|
||||
if i[:3] == b'\x06\x16\x12':
|
||||
temp.append(i)
|
||||
commands = temp
|
||||
# 处理不能正确被踢的问题
|
||||
|
||||
for i in commands:
|
||||
iv, ciphertext, tag = encrypt(user['key'], i, b'')
|
||||
# print(binascii.b2a_hex(i))
|
||||
|
||||
server.sendto(token + iv + tag[:12] +
|
||||
ciphertext, self.client_address)
|
||||
|
||||
|
||||
class TCP_handler(socketserver.StreamRequestHandler):
|
||||
def handle(self):
|
||||
self.data = self.rfile.readline().strip()
|
||||
|
||||
message = self.data.decode('utf-8')
|
||||
# print(message)
|
||||
data = message.split('|')
|
||||
if data[0] != Config.AUTHENTICATION:
|
||||
self.wfile.write(b'No authentication')
|
||||
return None
|
||||
|
||||
global clean_timer
|
||||
now = round(time.time() * 1000)
|
||||
if now - clean_timer >= Config.TIME_LIMIT:
|
||||
clean_timer = now
|
||||
memory_clean(now)
|
||||
|
||||
self.wfile.write(data_swap(data[1:]).encode('utf-8'))
|
||||
|
||||
|
||||
def data_swap(data: list) -> str:
|
||||
# data: list[str] = [command, ...]
|
||||
if data[0] == '1':
|
||||
# 开房
|
||||
# data = ['1', name, song_unlock, ]
|
||||
# song_unlock: base64 str
|
||||
name = data[1]
|
||||
song_unlock = base64.b64decode(data[2])
|
||||
|
||||
key = urandom(16)
|
||||
room_id = unique_random(room_id_dict)
|
||||
|
||||
room = Room()
|
||||
room.room_id = room_id
|
||||
room_id_dict[room_id] = room
|
||||
|
||||
player_id = unique_random(player_dict, 3)
|
||||
player = Player()
|
||||
player.player_id = player_id
|
||||
player.set_player_name(name)
|
||||
player_dict[player_id] = player
|
||||
|
||||
player.song_unlock = song_unlock
|
||||
room.song_unlock = song_unlock
|
||||
room.host_id = player_id
|
||||
room.players[0] = player
|
||||
room.player_num = 1
|
||||
|
||||
room_code = unique_random(room_code_dict, random_func=random_room_code)
|
||||
room.room_code = room_code
|
||||
room_code_dict[room_code] = room
|
||||
|
||||
token = room_id
|
||||
player.token = token
|
||||
|
||||
link_play_data[token] = {'key': key,
|
||||
'room': room,
|
||||
'player_index': 0,
|
||||
'player_id': player_id}
|
||||
|
||||
return '|'.join([str(x) for x in (0, room_code, room_id, token, base64.b64encode(key).decode('utf-8'), player_id)])
|
||||
|
||||
elif data[0] == '2':
|
||||
# 入房
|
||||
# data = ['2', name, song_unlock, room_code]
|
||||
# song_unlock: base64 str
|
||||
room_code = data[3].upper()
|
||||
|
||||
if room_code not in room_code_dict:
|
||||
# 房间号错误
|
||||
return '1202'
|
||||
|
||||
room = room_code_dict[room_code]
|
||||
if room.player_num == 4:
|
||||
# 满人
|
||||
return '1201'
|
||||
elif room.state != 2:
|
||||
# 无法加入
|
||||
return '1205'
|
||||
|
||||
name = data[1]
|
||||
song_unlock = base64.b64decode(data[2])
|
||||
|
||||
key = urandom(16)
|
||||
token = unique_random(link_play_data)
|
||||
player_id = unique_random(player_dict, 3)
|
||||
|
||||
player = Player()
|
||||
player.player_id = player_id
|
||||
player.set_player_name(name)
|
||||
player.token = token
|
||||
player_dict[player_id] = player
|
||||
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
|
||||
link_play_data[token] = {'key': key,
|
||||
'room': room,
|
||||
'player_index': player_index,
|
||||
'player_id': player_id}
|
||||
|
||||
return '|'.join([str(x) for x in (0, room_code, room.room_id, token, base64.b64encode(key).decode('utf-8'), player_id, base64.b64encode(room.song_unlock).decode('utf-8'))])
|
||||
|
||||
elif data[0] == '3':
|
||||
# 房间信息更新
|
||||
# data = ['3', token]
|
||||
token = int(data[1])
|
||||
if token in link_play_data:
|
||||
r = link_play_data[token]
|
||||
return '|'.join([str(x) for x in (0, r['room'].room_code, r['room'].room_id, base64.b64encode(r['key']).decode('utf-8'), r['room'].players[r['player_index']].player_id, base64.b64encode(r['room'].song_unlock).decode('utf-8'))])
|
||||
else:
|
||||
return '108'
|
||||
|
||||
|
||||
def link_play(ip: str = Config.HOST, udp_port: int = Config.UDP_PORT, tcp_port: int = Config.TCP_PORT):
|
||||
udp_server = socketserver.ThreadingUDPServer((ip, udp_port), UDP_handler)
|
||||
tcp_server = socketserver.ThreadingTCPServer((ip, tcp_port), TCP_handler)
|
||||
|
||||
threads = [threading.Thread(target=udp_server.serve_forever), threading.Thread(
|
||||
target=tcp_server.serve_forever)]
|
||||
[t.start() for t in threads]
|
||||
[t.join() for t in threads]
|
||||
180
latest version/linkplay_server/udp_class.py
Normal file
180
latest version/linkplay_server/udp_class.py
Normal file
@@ -0,0 +1,180 @@
|
||||
from .config import Config
|
||||
|
||||
|
||||
def b(value, length=1):
|
||||
return value.to_bytes(length=length, byteorder='little')
|
||||
|
||||
|
||||
def bi(value):
|
||||
return int.from_bytes(value, byteorder='little')
|
||||
|
||||
|
||||
class Player:
|
||||
def __init__(self) -> None:
|
||||
self.player_id = 0
|
||||
self.player_name = b'\x45\x6d\x70\x74\x79\x50\x6c\x61\x79\x65\x72\x00\x00\x00\x00\x00'
|
||||
self.token = 0
|
||||
|
||||
self.character_id = 0xff
|
||||
self.last_character_id = 0xff
|
||||
self.is_uncapped = 0
|
||||
|
||||
self.difficulty = 0xff
|
||||
self.last_difficulty = 0xff
|
||||
self.score = 0
|
||||
self.last_score = 0
|
||||
self.timer = 0
|
||||
self.last_timer = 0
|
||||
self.cleartype = 0
|
||||
self.last_cleartype = 0
|
||||
self.best_score_flag = 0
|
||||
self.best_player_flag = 0
|
||||
self.finish_flag = 0
|
||||
|
||||
self.player_state = 1
|
||||
self.download_percent = 0
|
||||
self.online = 0
|
||||
|
||||
self.last_timestamp = 0
|
||||
self.extra_command_queue = []
|
||||
|
||||
self.song_unlock: bytes = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
||||
|
||||
self.start_command_num = 0
|
||||
|
||||
def set_player_name(self, player_name: str):
|
||||
self.player_name = player_name.encode('ascii')
|
||||
if len(self.player_name) > 16:
|
||||
self.player_name = self.player_name[:16]
|
||||
else:
|
||||
self.player_name += b'\x00' * (16 - len(self.player_name))
|
||||
|
||||
|
||||
class Room:
|
||||
def __init__(self) -> None:
|
||||
self.room_id = 0
|
||||
self.room_code = 'AAAA00'
|
||||
|
||||
self.countdown = 0xffffffff
|
||||
self.timestamp = 0
|
||||
self.state = 0
|
||||
self.song_idx = 0xffff
|
||||
self.last_song_idx = 0xffff
|
||||
|
||||
self.song_unlock = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
|
||||
|
||||
self.host_id = 0
|
||||
self.players = [Player(), Player(), Player(), Player()]
|
||||
self.player_num = 0
|
||||
|
||||
self.interval = 1000
|
||||
self.times = 100
|
||||
|
||||
self.round_switch = 0
|
||||
|
||||
self.command_queue = []
|
||||
self.command_queue_length = 0
|
||||
|
||||
def get_players_info(self):
|
||||
# 获取所有玩家信息
|
||||
re = b''
|
||||
for i in self.players:
|
||||
re += b(i.player_id, 8) + b(i.character_id) + b(i.is_uncapped) + b(i.difficulty) + b(i.score, 4) + \
|
||||
b(i.timer, 4) + b(i.cleartype) + b(i.player_state) + \
|
||||
b(i.download_percent) + b(i.online) + b'\x00' + i.player_name
|
||||
return re
|
||||
|
||||
def get_player_last_score(self):
|
||||
# 获取上次曲目玩家分数,返回bytes
|
||||
if self.last_song_idx == 0xffff:
|
||||
return b'\xff\xff\x00\x00\x00\x00\x00\x00\x00' * 4
|
||||
re = b''
|
||||
|
||||
for i in range(4):
|
||||
player = self.players[i]
|
||||
|
||||
if player.player_id != 0:
|
||||
re += b(player.last_character_id) + b(player.last_difficulty) + b(player.last_score, 4) + b(
|
||||
player.last_cleartype) + b(player.best_score_flag) + b(player.best_player_flag)
|
||||
else:
|
||||
re += b'\xff\xff\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
return re
|
||||
|
||||
def make_round(self):
|
||||
# 轮换房主
|
||||
for i in range(4):
|
||||
if self.players[i].player_id == self.host_id:
|
||||
for j in range(1, 4):
|
||||
if self.players[(i + j) % 4].player_id != 0:
|
||||
self.host_id = self.players[(i + j) % 4].player_id
|
||||
break
|
||||
break
|
||||
|
||||
def delete_player(self, player_index: int):
|
||||
# 删除某个玩家
|
||||
self.player_num -= 1
|
||||
if self.players[player_index].player_id == self.host_id:
|
||||
self.make_round()
|
||||
|
||||
self.players[player_index].online = 0
|
||||
self.players[player_index] = Player()
|
||||
self.update_song_unlock()
|
||||
|
||||
def update_song_unlock(self):
|
||||
# 更新房间可用歌曲
|
||||
r = bi(b'\xff' * Config.LINK_PLAY_UNLOCK_LENGTH)
|
||||
for i in self.players:
|
||||
if i.player_id != 0:
|
||||
r &= bi(i.song_unlock)
|
||||
|
||||
self.song_unlock = b(r, Config.LINK_PLAY_UNLOCK_LENGTH)
|
||||
|
||||
def is_ready(self, old_state: int, player_state: int):
|
||||
# 是否全部准备就绪
|
||||
if self.state == old_state:
|
||||
for i in self.players:
|
||||
if i.player_id != 0 and (i.player_state != player_state or i.online == 0):
|
||||
return False
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_finish(self):
|
||||
# 是否全部进入结算
|
||||
if self.state == 8:
|
||||
return False
|
||||
|
||||
for i in self.players:
|
||||
if i.player_id != 0 and (i.finish_flag == 0 or i.online == 0):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def make_finish(self):
|
||||
# 结算
|
||||
self.state = 8
|
||||
self.last_song_idx = self.song_idx
|
||||
|
||||
max_score = 0
|
||||
max_score_i = []
|
||||
for i in range(4):
|
||||
player = self.players[i]
|
||||
if player.player_id != 0:
|
||||
player.finish_flag = 0
|
||||
player.last_timer = player.timer
|
||||
player.last_score = player.score
|
||||
player.last_cleartype = player.cleartype
|
||||
player.last_character_id = player.character_id
|
||||
player.last_difficulty = player.difficulty
|
||||
player.best_player_flag = 0
|
||||
|
||||
if player.last_score > max_score:
|
||||
max_score = player.last_score
|
||||
max_score_i = [i]
|
||||
elif player.last_score == max_score:
|
||||
max_score_i.append(i)
|
||||
|
||||
for i in max_score_i:
|
||||
self.players[i].best_player_flag = 1
|
||||
334
latest version/linkplay_server/udp_parser.py
Normal file
334
latest version/linkplay_server/udp_parser.py
Normal file
@@ -0,0 +1,334 @@
|
||||
import time
|
||||
|
||||
from .udp_class import Room, bi
|
||||
from .config import Config
|
||||
from .udp_sender import CommandSender
|
||||
|
||||
|
||||
class CommandParser:
|
||||
def __init__(self, room: Room, player_index: int = 0) -> None:
|
||||
self.room = room
|
||||
self.player_index = player_index
|
||||
|
||||
def get_commands(self, command):
|
||||
self.command = command
|
||||
l = {b'\x06\x16\x01': self.command_01,
|
||||
b'\x06\x16\x02': self.command_02,
|
||||
b'\x06\x16\x03': self.command_03,
|
||||
b'\x06\x16\x04': self.command_04,
|
||||
b'\x06\x16\x05': self.command_05,
|
||||
b'\x06\x16\x06': self.command_06,
|
||||
b'\x06\x16\x07': self.command_07,
|
||||
b'\x06\x16\x08': self.command_08,
|
||||
b'\x06\x16\x09': self.command_09,
|
||||
b'\x06\x16\x0a': self.command_0a,
|
||||
b'\x06\x16\x0b': self.command_0b
|
||||
}
|
||||
r = l[command[:3]]()
|
||||
|
||||
re = []
|
||||
|
||||
flag_13 = False
|
||||
for i in range(max(bi(self.command[12:16]), self.room.players[self.player_index].start_command_num), self.room.command_queue_length):
|
||||
if self.room.command_queue[i][:3] == b'\x06\x16\x13':
|
||||
if flag_13:
|
||||
break
|
||||
flag_13 = True
|
||||
re.append(self.room.command_queue[i])
|
||||
|
||||
if self.room.players[self.player_index].extra_command_queue:
|
||||
re += self.room.players[self.player_index].extra_command_queue
|
||||
self.room.players[self.player_index].extra_command_queue = []
|
||||
|
||||
if r:
|
||||
re += r
|
||||
|
||||
return re
|
||||
|
||||
def command_01(self):
|
||||
# 给房主
|
||||
player_id = bi(self.command[24:32])
|
||||
for i in self.room.players:
|
||||
if i.player_id == player_id and i.online == 1:
|
||||
self.room.host_id = player_id
|
||||
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_10())
|
||||
|
||||
return None
|
||||
|
||||
def command_02(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
song_idx = bi(self.command[24:26])
|
||||
|
||||
flag = 2
|
||||
if self.room.state == 2:
|
||||
flag = 0
|
||||
self.room.state = 3
|
||||
self.room.song_idx = song_idx
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_11())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return [x.command_0d(flag)]
|
||||
|
||||
def command_03(self):
|
||||
# 尝试进入结算
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player = self.room.players[self.player_index]
|
||||
player.score = bi(self.command[24:28])
|
||||
player.cleartype = self.command[28]
|
||||
player.difficulty = self.command[29]
|
||||
player.best_score_flag = self.command[30]
|
||||
player.finish_flag = 1
|
||||
player.last_timestamp -= Config.COMMAND_INTERVAL
|
||||
self.room.last_song_idx = self.room.song_idx
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
|
||||
if self.room.is_finish():
|
||||
self.room.make_finish()
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return None
|
||||
|
||||
def command_04(self):
|
||||
# 踢人
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player_id = bi(self.command[24:32])
|
||||
flag = 2
|
||||
if self.room.players[self.player_index].player_id == self.room.host_id and player_id != self.room.host_id:
|
||||
for i in range(4):
|
||||
if self.room.players[i].player_id == player_id:
|
||||
flag = 1
|
||||
self.room.delete_player(i)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
self.room.update_song_unlock()
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
break
|
||||
|
||||
return [x.command_0d(flag)]
|
||||
|
||||
def command_05(self):
|
||||
pass
|
||||
|
||||
def command_06(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
return None
|
||||
|
||||
def command_07(self):
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.players[self.player_index].song_unlock = self.command[24:536]
|
||||
self.room.update_song_unlock()
|
||||
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
return None
|
||||
|
||||
def command_08(self):
|
||||
self.room.round_switch = bi(self.command[24:25])
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return None
|
||||
|
||||
def command_09(self):
|
||||
re = []
|
||||
x = CommandSender(self.room)
|
||||
x.random_code = self.command[16:24]
|
||||
player = self.room.players[self.player_index]
|
||||
|
||||
if bi(self.command[12:16]) == 0:
|
||||
player.online = 1
|
||||
self.room.state = 1
|
||||
self.room.update_song_unlock()
|
||||
player.start_command_num = self.room.command_queue_length
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_15())
|
||||
else:
|
||||
if x.timestamp - player.last_timestamp >= Config.COMMAND_INTERVAL:
|
||||
re.append(x.command_0c())
|
||||
player.last_timestamp = x.timestamp
|
||||
|
||||
flag_13 = False
|
||||
# 离线判断
|
||||
for i in range(4):
|
||||
if i != self.player_index:
|
||||
t = self.room.players[i]
|
||||
if t.player_id != 0:
|
||||
if t.last_timestamp != 0:
|
||||
if t.online == 1 and x.timestamp - t.last_timestamp >= Config.PLAYER_PRE_TIMEOUT:
|
||||
t.online = 0
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
elif t.online == 0 and x.timestamp - t.last_timestamp >= Config.PLAYER_TIMEOUT:
|
||||
self.room.delete_player(i)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(i))
|
||||
flag_13 = True
|
||||
|
||||
flag_11 = False
|
||||
flag_12 = False
|
||||
|
||||
if player.online == 0:
|
||||
flag_12 = True
|
||||
player.online = 1
|
||||
|
||||
if self.room.is_ready(1, 1):
|
||||
flag_13 = True
|
||||
self.room.state = 2
|
||||
|
||||
if player.player_state != self.command[32]:
|
||||
flag_12 = True
|
||||
player.player_state = self.command[32]
|
||||
|
||||
if player.difficulty != self.command[33] and player.player_state != 5 and player.player_state != 6 and player.player_state != 7 and player.player_state != 8:
|
||||
flag_12 = True
|
||||
player.difficulty = self.command[33]
|
||||
|
||||
if player.cleartype != self.command[34] and player.player_state != 7 and player.player_state != 8:
|
||||
flag_12 = True
|
||||
player.cleartype = self.command[34]
|
||||
|
||||
if player.download_percent != self.command[35]:
|
||||
flag_12 = True
|
||||
player.download_percent = self.command[35]
|
||||
|
||||
if player.character_id != self.command[36]:
|
||||
flag_12 = True
|
||||
player.character_id = self.command[36]
|
||||
|
||||
if player.is_uncapped != self.command[37]:
|
||||
flag_12 = True
|
||||
player.is_uncapped = self.command[37]
|
||||
|
||||
if self.room.state == 3 and player.score != bi(self.command[24:28]):
|
||||
flag_12 = True
|
||||
player.score = bi(self.command[24:28])
|
||||
|
||||
if self.room.is_ready(3, 4):
|
||||
flag_13 = True
|
||||
self.room.countdown = Config.COUNTDOWM_TIME
|
||||
self.room.timestamp = round(time.time() * 1000)
|
||||
self.room.state = 4
|
||||
|
||||
if self.room.state == 4 or self.room.state == 5 or self.room.state == 6:
|
||||
timestamp = round(time.time() * 1000)
|
||||
self.room.countdown -= timestamp - self.room.timestamp
|
||||
self.room.timestamp = timestamp
|
||||
if self.room.state == 4 and self.room.countdown <= 0:
|
||||
# 此处不清楚
|
||||
self.room.state = 5
|
||||
self.room.countdown = 5999
|
||||
flag_11 = True
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.is_ready(5, 6):
|
||||
self.room.state = 6
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.is_ready(5, 7):
|
||||
self.room.state = 7
|
||||
self.room.countdown = 0xffffffff
|
||||
flag_13 = True
|
||||
|
||||
if self.room.state == 5 and self.room.countdown <= 0:
|
||||
print('我怎么知道这是啥')
|
||||
|
||||
if self.room.state == 6 and self.room.countdown <= 0:
|
||||
# 此处不清楚
|
||||
self.room.state = 7
|
||||
self.room.countdown = 0xffffffff
|
||||
flag_13 = True
|
||||
|
||||
if self.room.countdown <= 0:
|
||||
self.room.countdown = 0
|
||||
|
||||
if self.room.state == 7 or self.room.state == 8:
|
||||
if player.timer < bi(self.command[28:32]) or bi(self.command[28:32]) == 0 and player.timer != 0:
|
||||
player.last_timer = player.timer
|
||||
player.last_score = player.score
|
||||
player.timer = bi(self.command[28:32])
|
||||
player.score = bi(self.command[24:28])
|
||||
|
||||
if player.timer != 0 or self.room.state != 8:
|
||||
for i in self.room.players:
|
||||
i.extra_command_queue.append(
|
||||
x.command_0e(self.player_index))
|
||||
|
||||
if self.room.is_ready(8, 1):
|
||||
flag_13 = True
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
if self.room.round_switch == 1:
|
||||
self.room.make_round()
|
||||
|
||||
for i in self.room.players:
|
||||
i.timer = 0
|
||||
i.score = 0
|
||||
|
||||
if self.room.is_finish():
|
||||
# 有人退房导致的结算
|
||||
self.room.make_finish()
|
||||
flag_13 = True
|
||||
|
||||
if flag_11:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_11())
|
||||
if flag_12:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
if flag_13:
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
|
||||
return re
|
||||
|
||||
def command_0a(self):
|
||||
# 退出房间
|
||||
self.room.delete_player(self.player_index)
|
||||
|
||||
x = CommandSender(self.room)
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_12(self.player_index))
|
||||
|
||||
if self.room.state == 3 or self.room.state == 2:
|
||||
self.room.state = 1
|
||||
self.room.song_idx = 0xffff
|
||||
# self.room.command_queue_length += 1
|
||||
# self.room.command_queue.append(x.command_11())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_13())
|
||||
self.room.command_queue_length += 1
|
||||
self.room.command_queue.append(x.command_14())
|
||||
return None
|
||||
|
||||
def command_0b(self):
|
||||
# 推荐歌曲
|
||||
song_idx = bi(self.command[16:18])
|
||||
x = CommandSender(self.room)
|
||||
for i in range(4):
|
||||
if self.player_index != i and self.room.players[i].online == 1:
|
||||
self.room.players[i].extra_command_queue.append(
|
||||
x.command_0f(self.player_index, song_idx))
|
||||
|
||||
return None
|
||||
46
latest version/linkplay_server/udp_sender.py
Normal file
46
latest version/linkplay_server/udp_sender.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import time
|
||||
from .udp_class import Room, b
|
||||
|
||||
|
||||
class CommandSender:
|
||||
def __init__(self, room: Room = Room()) -> None:
|
||||
self.room = room
|
||||
self.timestamp = round(time.time() * 1000000)
|
||||
|
||||
self.random_code = b'\x11\x11\x11\x11\x00\x00\x00\x00'
|
||||
|
||||
def command_0c(self):
|
||||
return b'\x06\x16\x0c\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.state) + b(self.room.countdown, 4) + b(self.timestamp, 8) + b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
|
||||
|
||||
def command_0d(self, code: int):
|
||||
return b'\x06\x16\x0d\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(code) + b'\x07\x07\x07\x07\x07\x07\x07'
|
||||
|
||||
def command_0e(self, player_index: int):
|
||||
# 分数广播
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x0e\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + 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)
|
||||
|
||||
def command_0f(self, player_index: int, song_idx: int):
|
||||
# 歌曲推荐
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x0f\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + b(player.player_id, 8) + b(song_idx, 2) + b'\x06\x06\x06\x06\x06\x06'
|
||||
|
||||
def command_10(self):
|
||||
# 房主宣告
|
||||
return b'\x06\x16\x10\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + b(self.room.host_id, 8)
|
||||
|
||||
def command_11(self):
|
||||
return b'\x06\x16\x11\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.get_players_info() + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||
|
||||
def command_12(self, player_index: int):
|
||||
player = self.room.players[player_index]
|
||||
return b'\x06\x16\x12\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + 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)
|
||||
|
||||
def command_13(self):
|
||||
return b'\x06\x16\x13\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + 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) + b'\x01'
|
||||
|
||||
def command_14(self):
|
||||
return b'\x06\x16\x14\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + self.random_code + self.room.song_unlock + b'\x08\x08\x08\x08\x08\x08\x08\x08'
|
||||
|
||||
def command_15(self):
|
||||
return b'\x06\x16\x15\x09' + b(self.room.room_id, 8) + b(self.room.command_queue_length, 4) + 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) + b'\x09\x09\x09\x09\x09\x09\x09\x09\x09'
|
||||
Reference in New Issue
Block a user