def handle_device_page_read(self, type, seq, kwargs): page_id = kwargs.get('page_id') discard = kwargs.get('discard', True) if self.has_page(page_id): page = self.get_page(page_id) if discard: page.clear_buffer() if page.buffer_data_valid(): message = ServerMessage(Message.MSG_DEVICE_PAGE_READ, self.id(), seq, value=page) self.prepare_message(message) else: kwargs = {'page_id': page_id} command = ServerMessage(Message.CMD_DEVICE_PAGE_READ, self.id(), self.next_seq(seq), addr=page.addr(), size=page.size(), page_id=page_id) self.prepare_command(command) else: ServerError("Unknown page read {} requested".format(page_id)) self.nak_command(seq)
def handle_device_page_write(self, type, seq, kwargs): page_id = kwargs.get('page_id') offset = kwargs.get('offset', 0) value = kwargs.get('value') assert isinstance(value, (tuple, list, array.array)) if self.has_page(page_id): page = self.get_page(page_id) page_size = page.size() buf_size = page.data_length() buf = page.buf() st = None cmd_list = [] val = value[:page_size - offset] for i, v in enumerate(val): if i >= buf_size: #buf data is not enough if st is None: st = i break if v != buf[offset + i]: if st is None: st = i else: if st is not None: cmd = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE, self.id(), self.next_seq(seq), addr=page.addr() + st + offset, size=i - st, value=val[st:i], page_id=page_id) cmd_list.append(cmd) #build group command st = None if st: #last trunk cmd = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE, self.id(), self.next_seq(seq), addr=page.addr() + st + offset, size=len(val) - st, value=val[st:], page_id=page_id) cmd_list.append(cmd) if cmd_list: self.prepare_command(cmd_list) else: self.handle_nak_msg(seq, error="Not data changed") else: ServerError("Unknown page write {} requested".format(page_id)) self.nak_command(seq)
def handle_attached_msg(self, seq, data): val = data.get('value', None) if val: kwargs = {'repeat': LogicDevice.INTERVAL_POLL_DEVICE} command = ServerMessage(Message.CMD_POLL_DEVICE, self.id(), self.next_seq(Message.seq_root()), **kwargs) self.prepare_command(command) else: message = ServerMessage(Message.MSG_DEVICE_ATTACH, self.id(), seq, value=None) self.prepare_message(message)
def handle_nak_msg(self, seq, error=None): print(self.__class__.__name__, "handle NAK message:", seq, error) message = ServerMessage(Message.MSG_DEVICE_NAK, self.id(), seq, error=error) self.prepare_message(message)
def handle_page_write_msg(self, seq, cmd, data): type = cmd.type() cmd_info = cmd.extra_info() cmd_addr = cmd_info['addr'] cmd_value = cmd_info['value'] # store how many data will be sent cmd_group = cmd_info.pop('group', None) cmd_size = len(cmd_value) cmd_page_id = cmd_info['page_id'] #print(self.__class__.__name__, "handle_page_write_msg", cmd, data) data_size = data['value'] if data_size == 0 or data_size > cmd_size: #something error self.handle_nak_msg( seq, "data_size is invalid data_size {} cmd_size {}".format( data_size, cmd_size)) return page = self.get_page(cmd_page_id) start = cmd_addr - page.addr() result = page.save_to_buffer(start, cmd_value[:data_size]) if not result: self.handle_nak_msg( seq, "save_to_buffer error start {} data {}".format( start, data['value'])) return if data_size == len(cmd_value): if not cmd_group: message = ServerMessage(Message.MSG_DEVICE_PAGE_WRITE, self.id(), seq, value=page) self.prepare_message(message) else: self.prepare_command(cmd_group) else: #size = cmd_size - data_size value = cmd_value[data_size:] command = ServerMessage(Message.CMD_DEVICE_PAGE_WRITE, self.id(), self.next_seq(seq), addr=cmd_addr + data_size, value=value, page_id=cmd_page_id) self.prepare_command(command)
def handle_page_read_msg(self, seq, cmd, data): type = cmd.type() cmd_info = cmd.extra_info() cmd_addr = cmd_info['addr'] cmd_size = cmd_info['size'] # how many data is left cmd_page_id = cmd_info['page_id'] print(self.__class__.__name__, "handle_page_read_msg", cmd, data) data_size = len(data['value']) #current readout data size if data_size == 0 or data_size > cmd_size: #something error self.handle_nak_msg( seq, "data_size is invalid data_size {} cmd_size {}".format( data_size, cmd_size)) return page = self.get_page(cmd_page_id) start = cmd_addr - page.addr() result = page.save_to_buffer(start, data['value']) if not result: self.handle_nak_msg( seq, "save_to_buffer error start {} data {}".format( start, data['value'])) return if data_size == cmd_size: # no data left result = self.page_parse(cmd_page_id) if result: message = ServerMessage(Message.MSG_DEVICE_PAGE_READ, self.id(), seq, value=page) self.prepare_message(message) else: #failed page.clear_buffer() self.handle_nak_msg(seq, "parse page failed {}".format(cmd_page_id)) else: command = ServerMessage(Message.CMD_DEVICE_PAGE_READ, self.id(), self.next_seq(seq), addr=cmd_addr + data_size, size=cmd_size - data_size, page_id=cmd_page_id) self.prepare_command(command)
def handle_device_msg_output(self, type, seq, kwargs): page_id = (5, 0) if self.has_page(page_id): page = self.get_page(page_id) command = ServerMessage(Message.CMD_DEVICE_MSG_OUTPUT, self.id(), self.next_seq(seq), addr=page.addr(), size=page.size() - 1) self.prepare_command(command) else: ServerError("T5 is not ready for output") self.nak_command(seq)
def __init__(self): self.GamerAccount = GamerAccount(config.server.UsrAccountAddr) # 创建套接字 self.WelcomeSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置服务器使用固定地址 self.WelcomeSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定端口 self.WelcomeSocket.bind(('', config.connect.ServerPort)) self.UnloggedGamers = [] # socket已连接,但是逻辑还没有登录的玩家 self.Gamers = GamerGroup(config.game.MaxGamer) # 已登录的玩家群组 self.GameRoundPerGamer = config.game.MaxRound # 每一个玩家出题的循环次数 self.ServerMessage = ServerMessage() # 服务器端发送消息模板类 self.TimerManager = TimerManager() # 服务器端计时器管理实例,可以方便地创建定时器 self.GamerCmdListenThreads = [] # 游戏状态消息循环监听的消息接受线程列表 self.GameLogic = None # 游戏逻辑实体类 self.CmdQueue = Queue() # 监听消息线程间通信队列 self.GameBeginFlag = ThreadValue(True) # 指示游戏是否开始的标志 self.MessageLoopFlag = True # 指示消息循环是否退出的标志
def handle_connected_msg(self, seq, data): #print("handle_connected_msg") if LogicDevice.PAGE_ID_INFORMATION_QUERY_CONNECTED: page_id = Page.ID_INFORMATION if not self.page_valid(page_id): self.page_read(page_id, discard=True) address = 0x01 #FIXME: need report device Phy I2C Address message = ServerMessage(Message.MSG_DEVICE_CONNECTED, self.id(), seq, value=address) self.prepare_message(message)
def handle_attach_msg(self, id, seq, ext_info): logic_pipe = ext_info['value'] #remove device if id in self.devices.keys(): if not logic_pipe: dev = self.devices[id] del self.devices[id] del dev # logice device is removed, so there should send message to UI individually ServerMessage(Message.MSG_DEVICE_ATTACH, id, seq, value=None, pipe=self.ui_pipe).send() else: if logic_pipe: dev = LogicDevice( id, logic_pipe ) #each logic device will communicate to a physical device self.devices[id] = dev
def nak_command(self, seq): message = ServerMessage(Message.MSG_DEVICE_NAK, self.id(), seq) self.prepare_message(message)
def set_get_chip_info(self): kwargs = {'addr': 0, 'size': 7} command = ServerMessage(Message.CMD_DEVICE_PAGE_READ, self.id(), self.next_seq(Message.seq_root()), **kwargs) self.prepare_command(command)
def handle_interrupt_data_msg(self, seq, data): message = ServerMessage(Message.MSG_DEVICE_INTERRUPT_DATA, self.id(), seq, value=data['value']) self.prepare_message(message)
def handle_message_output_msg(self, seq, cmd, data): message = ServerMessage(Message.MSG_DEVICE_MSG_OUTPUT, self.id(), seq, value=data['value']) self.prepare_message(message)
def handle_raw_data_msg(self, seq, cmd, data): message = ServerMessage(Message.MSG_DEVICE_RAW_DATA, self.id(), seq, value=data['value']) self.prepare_message(message)
def handle_device_raw_send(self, type, seq, kwargs): command = ServerMessage(Message.CMD_DEVICE_RAW_DATA, self.id(), self.next_seq(seq), **kwargs) self.prepare_command(command)
def set_bridge_poll(self, kwargs={}): command = ServerMessage(Message.CMD_POLL_BRIDGE, self.id(), self.next_seq(Message.seq_root()), **kwargs) self.prepare_command(command)
def handle_device_poll(self, type, seq, kwargs): command = ServerMessage(Message.CMD_POLL_DEVICE, self.id(), self.next_seq(seq), **kwargs) self.prepare_command(command)
def set_raw_command(self, raw_data): kwargs = {'value': raw_data} command = ServerMessage(Message.CMD_DEVICE_RAW_DATA, self.id(), self.next_seq(Message.seq_root()), **kwargs) self.prepare_command(command)
class Server: def __init__(self): self.GamerAccount = GamerAccount(config.server.UsrAccountAddr) # 创建套接字 self.WelcomeSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置服务器使用固定地址 self.WelcomeSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定端口 self.WelcomeSocket.bind(('', config.connect.ServerPort)) self.UnloggedGamers = [] # socket已连接,但是逻辑还没有登录的玩家 self.Gamers = GamerGroup(config.game.MaxGamer) # 已登录的玩家群组 self.GameRoundPerGamer = config.game.MaxRound # 每一个玩家出题的循环次数 self.ServerMessage = ServerMessage() # 服务器端发送消息模板类 self.TimerManager = TimerManager() # 服务器端计时器管理实例,可以方便地创建定时器 self.GamerCmdListenThreads = [] # 游戏状态消息循环监听的消息接受线程列表 self.GameLogic = None # 游戏逻辑实体类 self.CmdQueue = Queue() # 监听消息线程间通信队列 self.GameBeginFlag = ThreadValue(True) # 指示游戏是否开始的标志 self.MessageLoopFlag = True # 指示消息循环是否退出的标志 def send_cmd_by_id(self, id_, command, **kwargs): gamer = self.Gamers.get_gamer_by_id(id_) gamer.send_cmd(command=command, **kwargs) def send_all_cmd(self, command, **kwargs): for g in self.Gamers: g.send_cmd(command, **kwargs) def send_all_cmd_unlogged(self, command, **kwargs): for g in self.UnloggedGamers: g.send_cmd(command, **kwargs) def run(self): logger.info('Server.run', 'Server is running now') self.login_state() # 对所有gamer,启动监听其请求的线程 for g in self.Gamers: t = threading.Thread(target=g.listen_gamer_command, args=(self.CmdQueue, ), daemon=True) self.GamerCmdListenThreads.append(t) for t in self.GamerCmdListenThreads: t.start() self.game_state() def game_state(self): logger.debug('server.gamer_state', 'entering game state') # 初始化游戏逻辑 self.GameLogic = GameLogic(len(self.Gamers)) for round_index in range(self.GameRoundPerGamer): # 游戏循环次数等于玩家数量 for cur_gamer_index in range(len(self.Gamers)): cur_gamer = self.Gamers[cur_gamer_index] self.GameLogic.init_game_state(cur_gamer.Id) self.send_all_cmd(**make_newround_command()) # 将当前出题者加入到已回答玩家列表中,防止其自己猜自己 self.GameLogic.add_answered_gamer_id(cur_gamer.Id) # 发送开始画图和通告画图者的通知 paint_message = self.ServerMessage.make_paint_inform_message( cur_gamer.UserName) self.send_all_cmd(**make_inform_command(paint_message)) # 当前画图者发出开始画图指令 cur_gamer.send_cmd(**make_begin_paint_command()) # 进入指令处理循环 self.MessageLoopFlag = True while self.MessageLoopFlag: msg = self.CmdQueue.get() # 阻塞队列,处理接受到的命令 try: cmd, cmd_body = decode_msg(msg, raise_exception=True) handler = get_handler(S_GAME_STATE, cmd) handler(self, cur_gamer=cur_gamer, raw_message=msg, **cmd_body) except DecodeError as de: logger.error( 'server.game_state', f'decoding error in game message loop: {de}') except Exception as e: she = ServerHandlingError(cmd, cmd_body, e) logger.error( 'server.game_state', f'unknown error when handling in game state: {she}' ) # 关闭游戏 self.close() def handle_host_begin_game_cmd(self, host_index=0): logger.debug('handle_host_cmd', 'host handling threading start!') while True: if len(self.UnloggedGamers) != 0: cmd, body = self.UnloggedGamers[host_index].recv_cmd() logger.debug('handle_host_cmd', f'cmd: {cmd}, body:{body}') handler = get_handler(S_LOGIN_STATE, cmd, supress_log=True) handler(self, gamer=self.UnloggedGamers[host_index], **body) # 该线程如果接收到了主机有关游戏开始的命令后就退出 if cmd == CMD_BEGIN_GAME: return else: time.sleep(1) def login_state(self): # 欢迎socket监听3秒就开始监听主机命令 self.WelcomeSocket.settimeout(config.server.ServerAcceptInterval) self.WelcomeSocket.listen(100) # todo: 划定最大连接数量 login_threads = [] # 外层循环接受玩家连接 while True: # 接受玩家连接的套接字 gamer = self.accept_new_gamer() if gamer is None: # accept内部逻辑会判断是否停止接受更多玩家,以None返回 break # 每一个连接的套接字都启用一个线程处理登录 login_thread = threading.Thread(None, target=gamer.check_login, args=(self, )) login_thread.start() # 主机线程处理 if len(self.UnloggedGamers) == 1: # 必须等到主机登录线程结束以后再启动主机的监听线程 # 同时,主机没有登录成功时,阻塞其他玩家的登录 login_thread.join() host_thread = threading.Thread( None, target=self.handle_host_begin_game_cmd, args=(0, )) login_threads.append(host_thread) host_thread.start() else: login_threads.append(login_thread) # 等待所有玩家登录就绪 # todo: 玩家登录目前没有超时检查 for login_thread in login_threads: login_thread.join() # 关闭欢迎socket self.WelcomeSocket.close() def accept_new_gamer(self): ''' 使用欢迎套接字来接受玩家连接,同时监听Host的开始游戏命令来终止 接受更多的玩家连接 :return: 玩家连接的套接字和地址,如果游戏开始则返回None ''' self.WelcomeSocket.settimeout( config.server.HostCommandInterval) # 设置accept退出的检查周期 while self.GameBeginFlag.get_val(): try: con_socket, addr = self.WelcomeSocket.accept() logger.info('Server.login', 'new gamer connecting...') gamer = UnloggedGamer(len(self.UnloggedGamers), addr, con_socket) # 接受到新玩家以后加入到gamer列表中 # 由于accept套接字的时候是串行的,因此不需要互斥读写未登录玩家列表 self.UnloggedGamers.append(gamer) return gamer # socket超时后检查游戏开始的全局flag是否被修改 except socket.timeout: pass return None def close(self): self.send_all_cmd(**make_end_game_command()) for gamer in self.Gamers: gamer.close() self.WelcomeSocket.close()