Rearrange Link Play module

- Try to ensure thread safety by using TCP socket
This commit is contained in:
Lost-MSth
2022-09-19 01:46:01 +08:00
parent b1575af0db
commit 6298b37ea2
16 changed files with 387 additions and 310 deletions

View File

@@ -38,7 +38,12 @@ class Constant:
DOWNLOAD_LINK_PREFIX = Config.DOWNLOAD_LINK_PREFIX
LINK_PLAY_UNLOCK_LENGTH = 512 # Units: bytes
LINK_PLAY_TIMEOUT = 10 # Units: seconds
LINK_PLAY_TIMEOUT = 5 # Units: seconds
LINK_PLAY_HOST = '127.0.0.1' if Config.SET_LINK_PLAY_SERVER_AS_SUB_PROCESS else Config.LINK_PLAY_HOST
LINK_PLAY_TCP_PORT = Config.LINK_PLAY_TCP_PORT
LINK_PLAY_UDP_PORT = Config.LINK_PLAY_UDP_PORT
LINK_PLAY_AUTHENTICATION = Config.LINK_PLAY_AUTHENTICATION
COURSE_STAMINA_COST = 4

View File

@@ -1,10 +1,13 @@
from base64 import b64encode
import socket
from base64 import b64decode, b64encode
from core.error import ArcError, Timeout
from .constant import Constant
from .user import UserInfo
socket.setdefaulttimeout(Constant.LINK_PLAY_TIMEOUT)
def get_song_unlock(client_song_map: dict) -> bytes:
'''处理可用歌曲bit返回bytes'''
@@ -82,9 +85,8 @@ class Room:
}
class LocalMultiPlayer:
def __init__(self, conn=None) -> None:
self.conn = conn
class RemoteMultiPlayer:
def __init__(self) -> None:
self.user: 'Player' = None
self.room: 'Room' = None
@@ -93,30 +95,44 @@ class LocalMultiPlayer:
def to_dict(self) -> dict:
return dict(self.room.to_dict(), **self.user.to_dict())
def data_swap(self, data: tuple) -> tuple:
self.conn.send(data)
if self.conn.poll(Constant.LINK_PLAY_TIMEOUT):
self.data_recv = self.conn.recv()
if self.data_recv[0] != 0:
raise ArcError('Link Play error.',
self.data_recv[0], status=400)
else:
@staticmethod
def tcp(data: str) -> str:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((Constant.LINK_PLAY_HOST,
Constant.LINK_PLAY_TCP_PORT))
sock.sendall(bytes(data + "\n", "utf-8"))
try:
received = str(sock.recv(1024), "utf-8").strip()
except socket.timeout:
raise Timeout(
'Timeout when waiting for data from local udp server.', status=400)
'Timeout when waiting for data from link play server.', status=400)
# print(received)
return received
def data_swap(self, data: tuple) -> tuple:
received = self.tcp(Constant.LINK_PLAY_AUTHENTICATION +
'|' + '|'.join([str(x) for x in data]))
self.data_recv = received.split('|')
if self.data_recv[0] != '0':
raise ArcError('Link Play error.',
int(self.data_recv[0]), status=400)
def create_room(self, user: 'Player' = None) -> None:
'''创建房间'''
if user is not None:
self.user = user
user.select_user_one_column('name')
self.data_swap((1, self.user.name, self.user.song_unlock))
self.data_swap((1, self.user.name, b64encode(
self.user.song_unlock).decode('utf-8')))
self.room = Room()
self.room.room_code = self.data_recv[1]
self.room.room_id = self.data_recv[2]
self.room.room_id = int(self.data_recv[2])
self.room.song_unlock = self.user.song_unlock
self.user.token = self.data_recv[3]
self.user.key = self.data_recv[4]
self.user.player_id = self.data_recv[5]
self.user.token = int(self.data_recv[3])
self.user.key = b64decode(self.data_recv[4])
self.user.player_id = int(self.data_recv[5])
def join_room(self, room: 'Room' = None, user: 'Player' = None) -> None:
'''加入房间'''
@@ -127,13 +143,13 @@ class LocalMultiPlayer:
self.user.select_user_one_column('name')
self.data_swap(
(2, self.user.name, self.user.song_unlock, room.room_code))
(2, self.user.name, b64encode(self.user.song_unlock).decode('utf-8'), room.room_code))
self.room.room_code = self.data_recv[1]
self.room.room_id = self.data_recv[2]
self.room.song_unlock = self.data_recv[6]
self.user.token = self.data_recv[3]
self.user.key = self.data_recv[4]
self.user.player_id = self.data_recv[5]
self.room.room_id = int(self.data_recv[2])
self.room.song_unlock = b64decode(self.data_recv[6])
self.user.token = int(self.data_recv[3])
self.user.key = b64decode(self.data_recv[4])
self.user.player_id = int(self.data_recv[5])
def update_room(self, user: 'Player' = None) -> None:
'''更新房间'''
@@ -142,7 +158,7 @@ class LocalMultiPlayer:
self.data_swap((3, self.user.token))
self.room = Room()
self.room.room_code = self.data_recv[1]
self.room.room_id = self.data_recv[2]
self.room.song_unlock = self.data_recv[5]
self.user.key = self.data_recv[3]
self.user.player_id = self.data_recv[4]
self.room.room_id = int(self.data_recv[2])
self.room.song_unlock = b64decode(self.data_recv[5])
self.user.key = b64decode(self.data_recv[3])
self.user.player_id = int(self.data_recv[4])

View File

@@ -0,0 +1 @@
from .main import link_play

View File

@@ -1,11 +1,11 @@
import os
from os import urandom
from cryptography.hazmat.primitives.ciphers import (
Cipher, algorithms, modes
)
def encrypt(key, plaintext, associated_data):
iv = os.urandom(12)
iv = urandom(12)
encryptor = Cipher(
algorithms.AES(key),
modes.GCM(iv, min_tag_length=12),

View 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

View 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]

View File

@@ -1,4 +1,4 @@
from .udp_config import Config
from .config import Config
def b(value, length=1):
@@ -38,7 +38,7 @@ class Player:
self.last_timestamp = 0
self.extra_command_queue = []
self.song_unlock = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
self.song_unlock: bytes = b'\x00' * Config.LINK_PLAY_UNLOCK_LENGTH
self.start_command_num = 0

View File

@@ -1,7 +1,7 @@
import time
from .udp_class import Room, bi
from .udp_config import Config
from .config import Config
from .udp_sender import CommandSender
@@ -311,7 +311,7 @@ class CommandParser:
self.room.command_queue_length += 1
self.room.command_queue.append(x.command_12(self.player_index))
if self.room.state == 3:
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

View File

@@ -142,13 +142,15 @@ def main():
except:
app.logger.warning('Initialization error!')
if Config.UDP_PORT and Config.UDP_PORT != '':
from server.multiplayer import conn2
from udpserver.udp_main import link_play
if Config.LINK_PLAY_HOST and Config.SET_LINK_PLAY_SERVER_AS_SUB_PROCESS:
from linkplay_server import link_play
process = [Process(target=link_play, args=(
conn2, Config.HOST, int(Config.UDP_PORT)))]
Config.LINK_PLAY_HOST, int(Config.LINK_PLAY_UDP_PORT), int(Config.LINK_PLAY_TCP_PORT)))]
[p.start() for p in process]
app.logger.info("UDP server is running...")
app.logger.info("Link Play UDP server is running on " +
Config.LINK_PLAY_HOST + ':' + str(Config.LINK_PLAY_UDP_PORT) + " ...")
app.logger.info("Link Play TCP server is running on " +
Config.LINK_PLAY_HOST + ':' + str(Config.LINK_PLAY_TCP_PORT) + " ...")
tcp_server_run()
[p.join() for p in process]
else:

View File

@@ -0,0 +1,9 @@
import os
import sys
import linkplay_server
os.chdir(sys.path[0])
if __name__ == '__main__':
linkplay_server.link_play()

View File

@@ -1,7 +1,5 @@
from multiprocessing import Pipe
from core.error import ArcError
from core.linkplay import LocalMultiPlayer, Player, Room
from core.linkplay import Player, RemoteMultiPlayer, Room
from core.sql import Connect
from flask import Blueprint, request
from setting import Config
@@ -11,24 +9,23 @@ from .func import arc_try, success_return
bp = Blueprint('multiplayer', __name__, url_prefix='/multiplayer')
conn1, conn2 = Pipe()
@bp.route('/me/room/create', methods=['POST']) # 创建房间
@auth_required(request)
@arc_try
def room_create(user_id):
if not Config.UDP_PORT or Config.UDP_PORT == '':
raise ArcError('The local udp server is down.', 151, status=404)
if not Config.LINK_PLAY_HOST:
raise ArcError('The link play server is unavailable.', 151, status=404)
with Connect() as c:
x = LocalMultiPlayer(conn1)
x = RemoteMultiPlayer()
user = Player(c, user_id)
user.get_song_unlock(request.json['clientSongMap'])
x.create_room(user)
r = x.to_dict()
r['endPoint'] = request.host.split(
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
r['port'] = int(Config.UDP_PORT)
':')[0] if Config.LINK_PLAY_DISPLAY_HOST == '' else Config.LINK_PLAY_DISPLAY_HOST
r['port'] = int(Config.LINK_PLAY_UDP_PORT)
return success_return(r)
@@ -36,11 +33,11 @@ def room_create(user_id):
@auth_required(request)
@arc_try
def room_join(user_id, room_code):
if not Config.UDP_PORT or Config.UDP_PORT == '':
raise ArcError('The local udp server is down.', 151, status=404)
if not Config.LINK_PLAY_HOST:
raise ArcError('The link play server is unavailable.', 151, status=404)
with Connect() as c:
x = LocalMultiPlayer(conn1)
x = RemoteMultiPlayer()
user = Player(c, user_id)
user.get_song_unlock(request.json['clientSongMap'])
room = Room()
@@ -48,8 +45,8 @@ def room_join(user_id, room_code):
x.join_room(room, user)
r = x.to_dict()
r['endPoint'] = request.host.split(
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
r['port'] = int(Config.UDP_PORT)
':')[0] if Config.LINK_PLAY_DISPLAY_HOST == '' else Config.LINK_PLAY_DISPLAY_HOST
r['port'] = int(Config.LINK_PLAY_UDP_PORT)
return success_return(r)
@@ -57,16 +54,16 @@ def room_join(user_id, room_code):
@auth_required(request)
@arc_try
def multiplayer_update(user_id):
if not Config.UDP_PORT or Config.UDP_PORT == '':
raise ArcError('The local udp server is down.', 151, status=404)
if not Config.LINK_PLAY_HOST:
raise ArcError('The link play server is unavailable.', 151, status=404)
with Connect() as c:
x = LocalMultiPlayer(conn1)
x = RemoteMultiPlayer()
user = Player(c, user_id)
user.token = int(request.json['token'])
x.update_room(user)
r = x.to_dict()
r['endPoint'] = request.host.split(
':')[0] if Config.LINK_PLAY_HOST == '' else Config.LINK_PLAY_HOST
r['port'] = int(Config.UDP_PORT)
':')[0] if Config.LINK_PLAY_DISPLAY_HOST == '' else Config.LINK_PLAY_DISPLAY_HOST
r['port'] = int(Config.LINK_PLAY_UDP_PORT)
return success_return(r)

View File

@@ -30,29 +30,27 @@ class Config():
Allowed game versions
If it is blank, all are allowed.
'''
ALLOW_APPVERSION = ['3.12.6', '3.12.6c', '4.0.256', '4.0.256c', '4.0.255', '4.0.255c']
ALLOW_APPVERSION = ['3.12.6', '3.12.6c',
'4.0.256', '4.0.256c', '4.0.255', '4.0.255c']
'''
--------------------
'''
'''
--------------------
联机功能的端口号,若为空,则默认不开启联机功能
Port of your link play server
If it is blank, link play will be unavailable.
联机功能相关设置请确保与Link Play服务器端的设置一致
Setting of your link play server
Please ensure that the settings on the side of Link Play server are consistent.
'''
UDP_PORT = '10900'
'''
--------------------
'''
'''
--------------------
联机功能地址,留空则自动获取
Link Play address
If left blank, it will be obtained automatically.
'''
LINK_PLAY_HOST = '' # ***.com
# SET_LINK_PLAY_SERVER_AS_SUB_PROCESS: If it is `True`, the link play server will run with the main server locally at the same time.
SET_LINK_PLAY_SERVER_AS_SUB_PROCESS = True
# LINK_PLAY_HOST: If it is blank, the link play feature will be disabled.
LINK_PLAY_HOST = '0.0.0.0'
LINK_PLAY_UDP_PORT = 10900
LINK_PLAY_TCP_PORT = 10901
LINK_PLAY_AUTHENTICATION = 'my_link_play_server'
# LINK_PLAY_DISPLAY_HOST: If it is blank, the host of link play server for the client will be obtained automatically.
LINK_PLAY_DISPLAY_HOST = ''
'''
--------------------
'''

View File

@@ -1,11 +0,0 @@
class Config:
TIME_LIMIT = 3600000
COMMAND_INTERVAL = 1000000
COUNTDOWM_TIME = 3999
PLAYER_PRE_TIMEOUT = 3000000
PLAYER_TIMEOUT = 20000000
LINK_PLAY_UNLOCK_LENGTH = 512

View File

@@ -1,223 +0,0 @@
import socketserver
import threading
import os
import random
import time
# import binascii
from . import aes
from .udp_parser import CommandParser
from .udp_class import Room, Player, bi
from .udp_config import Config
# 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
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 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):
# 内存清理
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)
class UDPhandler(socketserver.BaseRequestHandler):
def handle(self):
client_msg, server = self.request
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 = aes.decrypt(user['key'], b'', iv, ciphertext, tag)
# 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 = aes.encrypt(user['key'], i, b'')
# print(binascii.b2a_hex(i))
server.sendto(token + iv + tag[:12] +
ciphertext, self.client_address)
def server_run(ip, port):
server = socketserver.ThreadingUDPServer((ip, port), UDPhandler)
server.serve_forever()
def data_swap(conn):
clean_timer = 0
while True:
try:
data = conn.recv()
except EOFError:
break
now = round(time.time() * 1000)
if now - clean_timer >= Config.TIME_LIMIT:
clean_timer = now
memory_clean(now)
if data[0] == 1:
# 开房
key = os.urandom(16)
room_id = bi(os.urandom(8))
while room_id in room_id_dict and room_id == 0:
room_id = bi(os.urandom(8))
room = Room()
room.room_id = room_id
room_id_dict[room_id] = room
player_id = bi(os.urandom(3))
while player_id in player_dict and player_id == 0:
player_id = bi(os.urandom(3))
player = Player()
player.player_id = player_id
player.set_player_name(data[1])
player_dict[player_id] = player
player.song_unlock = data[2]
room.song_unlock = data[2]
room.host_id = player_id
room.players[0] = player
room.player_num = 1
room_code = random_room_code()
while room_code in room_code_dict:
room_code = 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}
conn.send((0, room_code, room_id, token, key, player_id))
elif data[0] == 2:
room_code = data[3].upper()
if room_code not in room_code_dict:
# 房间号错误
conn.send((1202, ))
else:
room = room_code_dict[room_code]
if room.player_num == 4:
# 满人
conn.send((1201, ))
elif room.state != 2:
# 无法加入
conn.send((1205, ))
else:
key = os.urandom(16)
token = bi(os.urandom(8))
player_id = bi(os.urandom(3))
while player_id in player_dict and player_id == 0:
player_id = bi(os.urandom(3))
player = Player()
player.player_id = player_id
player.set_player_name(data[1])
player.token = token
player_dict[player_id] = player
player.song_unlock = data[2]
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}
conn.send((0, room_code, room.room_id,
token, key, player_id, room.song_unlock))
elif data[0] == 3:
token = data[1]
if token in link_play_data:
r = link_play_data[token]
conn.send((0, r['room'].room_code, r['room'].room_id, r['key'],
r['room'].players[r['player_index']].player_id, r['room'].song_unlock))
else:
conn.send((108, ))
def link_play(conn, ip: str, port: int):
try:
server = threading.Thread(target=server_run, args=(ip, port))
data_exchange = threading.Thread(target=data_swap, args=(conn,))
server.start()
data_exchange.start()
except:
pass