Esempio n. 1
0
    def login_thread(self, socket_obj, con_addr):
        socket_obj.settimeout(None)
        # 先出于等待登录状态
        while True:
            cmd, vals = recv_cmd(socket_obj, decode=True)
            if cmd == 'Login':
                try:
                    user_name = vals['Username']
                    password = vals['Password']
                    passed, login_info = self.check_login(user_name, password)
                except KeyError:
                    logger.error('server.login',
                                 'Login指令中缺少 username 和 password 项!')
                    continue
                send_cmd(socket_obj,
                         command='Login',
                         LoginStateCode=1 if passed else 0,
                         LoginMessage=login_info,
                         ID=id)

                if passed:
                    logger.info('server.login', 'sending gamer info...')

                    self.add_gamer(socket_obj, con_addr, user_name)
                    # todo: 修改为Gamer实现
                    self.sendGamerInfo()
                    return

            # 玩家主动或者被动放弃登录
            elif cmd == 'ExitLogin':
                return
Esempio n. 2
0
def handle_make_puzzle(server, **kwargs):
    answer, hint = parse_make_puzzle_command(kwargs)
    # 将谜底和提示交给游戏逻辑实体管理
    server.GameLogic.set_answer_hint(answer, hint)
    # 启动本轮游戏的倒计时计时器,每一秒在命令队列中加入一条tick指令
    server.TimerManager.start_gametime_count_timer(
        timer_name=GAME_DOWNCOUNT_TIMER,
        msg_queue=server.CmdQueue,
        time_interval=1,
        max_ticks=config.game.RoundTime)
    # 将提示发送给所有玩家
    server.send_all_cmd(**make_inform_command(f'提示: {hint}'))

    logger.info('game_handlers.handle_make_puzzle',
                f'puzzle has been made, answer: {answer}, hint:{hint}')
Esempio n. 3
0
    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()
Esempio n. 4
0
    def game(self):
        while not self.Engine.GameEndFlag:
            try:
                cmd, vals = self.Engine.recv_cmd()

                logger.info('client.core.game', f'cmd: {cmd}, vals: {vals}')

                handler = get_handler(C_GAME_STATE, cmd)
                handler(self.Engine, self.Signals, **vals)

                logger.info('client.core.game', f'cmd: {cmd} executed')

            except BaseException as e:
                logger.error('client.core.game', 'err when game: {}'.format(e))

        self.exit()
Esempio n. 5
0
def handle_game_chat(server, **kwargs):
    # 如果所有玩家都猜对,则终止当前游戏循环的回调函数

    name, id_, cont = parse_chat_command(kwargs)
    is_answered, answered_num = server.GameLogic.process_answer(
        answer=cont, answer_gamer_id=id_, gamer_group=server.Gamers)

    # 如果猜对,则发送服务器通告所有玩家,不会告知聊天信息
    if is_answered:
        logger.info('game_handlers.handle_game_chat',
                    f'gamer {name} has answered the question')
        server_chat_msg = server.ServerMessage.make_gamer_answered_inform_message(
            name)
        server.send_all_cmd(**make_chat_command(
            id_=-1, name=SERVER_CHAT_NAME, content=server_chat_msg))
        # 刷新分数
        server.send_all_cmd(
            **make_gamer_info_command(server.Gamers.pack_all_gamers_info()))
    # 否则将chat视为一般的聊天,广播给所有玩家
    else:
        server.send_all_cmd(
            **make_chat_command(id_=id_, name=name, content=cont))

    # 如果所有玩家都猜对了
    if answered_num == len(server.Gamers):
        logger.info('game_handlers.handle_game_chat',
                    f'all gamers have answered the question, this round ends')

        # 1. 终止游戏倒计时
        server.TimerManager.stop_timers_by_name(GAME_DOWNCOUNT_TIMER)
        cur_gamer = server.Gamers.get_gamer_by_id(
            server.GameLogic.CurrentPaintingGamerId)
        # 2. 向当前画图玩家发出停止画图的指令,向所有玩家发出当前游戏round结束的通告
        cur_gamer.send_cmd(**make_stop_paint_command())
        server.send_all_cmd(**make_inform_command(
            server.ServerMessage.make_all_gamers_answered_message(
                server.GameLogic.Answer)))
        # 3. 设置flag终止消息监听循环,同时放入一条信息刷新循环判断
        server.MessageLoopFlag = False
        # 只取解码信息的body,不取包含body大小的头部信息,因为是本地的直接指令存入
        break_msg_loop_cmd, _ = encode_msg(**make_break_message_loop_command())
        server.CmdQueue.put(break_msg_loop_cmd)
        # 4. 将答案置为无效,防止其他玩家在谜底揭晓处于无效状态时进行答题
        server.GameLogic.set_answer_valid_state(False)
Esempio n. 6
0
def handle_gamer_login(server, **kwargs):
    gamer = extract_kwargs(kwargs, ('Gamer', 'gamer'), 'server.login_handler')
    # 首先检查玩家数量是否超过限制
    with server.Gamers as gp:
        is_gamer_enough = gp.check_gamer_is_enough()
        if is_gamer_enough:
            fail_cmd = make_login_result_command(status_code=0,
                                                 login_message='玩家数量超过限制',
                                                 ID=-1)
            gamer.send_cmd(**fail_cmd)
            return

    username, passwd = parse_login_command(kwargs)
    passed, login_msg, gamer_id = server.GamerAccount.check_gamer_login(
        username, passwd)

    cmd = make_login_result_command(status_code=1 if passed else 0,
                                    login_message=login_msg,
                                    ID=gamer_id)
    logger.debug('handlers.handle_gamer_login', f'send msg: {cmd}')

    if passed:
        from server.gamer import Gamer  # 逻辑内部import,防止循环import问题
        new_gamer = Gamer(gamer, gamer_id, username)
        with server.Gamers as gp:
            # 首先发送登录结果信息
            gamer.send_cmd(**cmd)
            gp.add_gamer(new_gamer, gamer_id, safe_call=True)
            # 然后发送所有玩家信息,更新玩家情况
            gamers_info = gp.pack_all_gamers_info()
            server.send_all_cmd(**make_gamer_info_command(gamers_info))

        # 登录成功后将flag标志为True告知外部可以退出登录循环
        gamer.LoginFlag.write_val(True)
        logger.info('server.login',
                    'gamer {}:{} login'.format(gamer_id, username))
    else:
        # 不通过也要发送登录结果
        gamer.send_cmd(**cmd)
Esempio n. 7
0
 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
Esempio n. 8
0
    def process_answer(self, answer, answer_gamer_id, gamer_group):
        logger.debug('GameLogic.process_answer',
                     f'state: {self.IsAnswerValid}, {answer} : {self.Answer}')
        # 问题处于无效状态时不做判断
        if not self.IsAnswerValid:
            return False, len(self.AnsweredGamerIds)

        if not self.check_gamer_is_answered(
                answer_gamer_id) and self.check_answer(answer):
            ans_gamer = gamer_group.get_gamer_by_id(answer_gamer_id)
            pat_gamer = gamer_group.get_gamer_by_id(
                self.CurrentPaintingGamerId)
            logger.info(
                'handlers.handle_game_chat',
                f'gamer {ans_gamer.UserName} has answered this puzzle')
            # 将答对的玩家的id加入到已完成回答的列表中
            self.add_answered_gamer_id(answer_gamer_id)
            # 答题玩家和画图玩家都得分
            ans_gamer.score(self.get_next_point())
            pat_gamer.score(config.game.DrawPoint)
            return True, len(self.AnsweredGamerIds)
        else:
            return False, len(self.AnsweredGamerIds)
Esempio n. 9
0
    def activate(self, socket_obj, id_, usrname):
        try:
            self.Signals = ClientSignal()
            self.init_slots()
            self.Engine = ClientEngine(self.Signals, socket_obj)
            self.Engine.set_gamer_name_id(id_, usrname)
            logger.info('client.activate', 'activating...')

            cmd, gamers = self.Engine.recv_cmd()
            while cmd != 'GamerInfo':
                time.sleep(1)  # 忽略其他指令
                cmd, gamers = self.Engine.recv_cmd()

            for gname, gscore in gamers['gamers']:
                self.Engine.add_gamer(gname)

            # 启动一个额外的线程用于background任务
            self.GameThread.start()
            self.Engine.show()

        except Exception as e:
            logger.critical('client.core.activate',
                            'Err when activate: {}'.format(e))
            raise e
Esempio n. 10
0
    def accept_and_monitor_host(self):
        '''
        使用欢迎套接字来接受玩家连接,同时监听Host的开始游戏命令来终止
        接受更多的玩家连接
        :return: 玩家连接的套接字和地址,如果游戏开始则返回None
        '''
        # 内层循环不断在监听主机命令和监听玩家连接之间循环
        while True:
            try:
                # print('accepting...')\
                con_socket, addr = self.Socket.accept()
                print('new gamer connected...')
                # 接受到新玩家以后,本轮循环不再监听主机命令
                return con_socket, addr
            # 每3秒就切换到监听主机命令
            except socket.timeout:
                if self.CntedUsrNumber != 0:
                    self.UsrSocket[0].settimeout(
                        config.server.HostCommandInterval)
                    try:
                        host_cmd, host_cmd_vals = self.recv_cmd(0, decode=True)

                        # 如果主机发出开始游戏命令,则返回None代表不再接受玩家连接
                        if host_cmd == 'BeginGame':
                            logger.info('server.accept', 'Game has begun!')
                            self.send_all_cmd(command='BeginGame')
                            # for i, s_ in enumerate(self.UsrSocket):
                            #     self.send_cmd(i, command='BeginGame')
                            return None, None
                        else:
                            logger.warning(
                                'server.accept',
                                f'Not accepted command type during login waiting: {host_cmd}, ignore'
                            )
                    except socket.timeout:
                        pass
Esempio n. 11
0
def handle_begin_paint(engine: ClientEngine, signals: ClientSignal, **kwargs):

    try:
        engine.Panel.set_painting(True)
        engine.Panel.State = 'painting'
        logger.info('client.handlers.handle_begin_paint',
                    'getting answer and hint')
        answer = engine.Panel.get_input_by_dialog('出题', '请输入谜底', '谜底不能为空',
                                                  True, lambda s: len(s) <= 20,
                                                  '谜底长度不能超过20个字符')
        hint = engine.Panel.get_input_by_dialog('出题', '请输入提示', '', False,
                                                lambda s: len(s) <= 20,
                                                '提示长度不能超过20个字符')

        logger.info('client.handlers.handle_begin_paint',
                    '谜题: {}, 提示: {}'.format(answer, hint))

        # engine.send_cmd(command=CMD_BEGIN_PAINT, answer=answer, hint=hint)
        engine.send_cmd(**make_make_puzzle_command(answer, hint))
        # 只有要画图的人才能看到设置面板
        engine.Panel.PaintPanel.set_setting_visible(True)

    except BaseException as e:
        logger.error('client.handlers.handle_begin_paint', 'err: {}'.format(e))
Esempio n. 12
0
    def login(self):
        try:
            usrName, password = self.getUsrAndPsw()

            logger.info('LoginPanel.login',
                        'username: {}, psw: {}'.format(usrName, password))
            self.send_cmd(**make_login_command(usrName, password))
            # self.send_cmd(command='Login', Username=usrName, Password=password)
            # 收取服务器的回复

            while True:
                cmd, body = self.recv_cmd()
                if cmd == CMD_LOGIN_RESULT:
                    break
                else:
                    logger.warning(
                        'LoginPanel.login',
                        'recv {} cmd with {}, skip'.format(cmd, body))
                    time.sleep(1)

            logger.info('LoginPanel.login', 'login success: {}'.format(body))

            # login_status_code = body['LoginStateCode']
            # login_info = body['LoginMessage']
            login_status_code, login_info, ID = parse_login_result_command(
                body)
            self.ClientId = ID
            if login_status_code == 1:
                self.close()
                logger.info('LoginPanel.login', 'activating...')
                self.Activator(self.Socket, self.ClientId, self.UsrName.text())
            else:
                widgets.QMessageBox.warning(self, '登陆失败', login_info)
                # self.UsrName.clear()
                self.Psw.clear()
        except Exception as e:
            logger.error('LoginPanel.login',
                         'Fail to login, err: {}'.format(e))
            raise e
Esempio n. 13
0
def handle_game_begin(server, **kwargs):
    logger.info('server.handle_game_begin', 'game is begun')
    # 设置游戏开始flag,停止接受更多玩家连接
    server.GameBeginFlag.write_val(False)
    # 向所有玩家发送游戏开始指令
    server.send_all_cmd_unlogged(**make_begin_game_command())
Esempio n. 14
0
    def game_state_(self):
        print('entering game state')
        for cur_gamer_index in range(self.CntedUsrNumber):
            # 每开始一轮游戏,先调用初始化方法
            self.initGameState()

            # 向所有玩家发出新一轮游戏的指令来初始化
            self.send_all_cmd(command='NewRound')

            # 将画图者添加到已完成玩家列表中,防止其自己猜自己
            self.AnsweredGamer.append(cur_gamer_index)

            # 发送游戏提示
            for i, s_ in enumerate(self.UsrSocket):
                self.send_cmd(i,
                              command='Inform',
                              Content='现在由 {name} 画图'.format(
                                  name=self.UsrName[cur_gamer_index]))

            # time.sleep(1)

            # 向当前画图玩家发出开始画图的指令
            self.send_cmd(cur_gamer_index, command='BeginPaint')

            # # 接受画图玩家出的题
            # cmd, vals = decode_msg(self.recv_cmd(cur_gamer_index))
            # if cmd

            print('thread starting from parent thread!')
            while True:
                msg = self.CmdQueue.get()  # 阻塞队列,处理接受到的命令
                cmd, vals = decode_msg(msg)
                if cmd != '':
                    logger.info('server.game',
                                'cmd: {}, vals: {}'.format(cmd, vals))

                if cmd == 'BeginPaint':
                    self.Answer = vals['answer']
                    self.Hint = vals['hint']
                    # 启动倒计时定时器
                    self.\
                        startTimer(timerId=0, downCount=int(config.game.RoundTime), interval=1)
                    print('timer starting!')

                elif cmd == 'Chat':
                    usr_id = int(vals['id'])
                    content = vals['content']

                    if usr_id not in self.AnsweredGamer and self.checkAnswer(
                            content):
                        # 一旦玩家的答案正确
                        print('用户%d的答案%s正确' % (usr_id, content))

                        self.AnsweredGamer.append(usr_id)
                        self.gamerScore(hid=cur_gamer_index, uid=usr_id)
                        self.sendGamerInfo()
                        self.send_all_cmd(command='Chat',
                                          Name='服务器',
                                          Content='%s 已经猜对了答案' %
                                          self.UsrName[usr_id])
                        print('游戏进度:', len(self.AnsweredGamer),
                              len(self.UsrPoint))

                        # 如果所有玩家都猜对了
                        if len(self.AnsweredGamer) == len(self.UsrPoint):
                            self.stopTimer(0)
                            # 当前玩家停止画图
                            self.send_cmd(cur_gamer_index, command='StopPaint')
                            # 跳到下一个玩家
                            break

                    # 只有不是正确答案的聊天才会被公布
                    else:
                        self.send_all_cmd(command='Chat', **vals)

                elif cmd == 'Timeout':
                    # for i in range(len(self.UsrSocket)):
                    #     self.send_cmd(i, command='Inform', Content='时间到')
                    self.send_all_cmd(command='Inform', Content='时间到')
                    self.send_cmd(cur_gamer_index, command='StopPaint')
                    time.sleep(2)
                    break

                elif cmd == 'PaintPoint' or \
                        cmd == 'ClickPoint':
                    for i_, s_ in enumerate(self.UsrSocket):
                        if i_ != cur_gamer_index:
                            self.send_cmd(i_, command=cmd, **vals)

                elif cmd == 'TimerEvent':
                    for i_, s_ in enumerate(self.UsrSocket):
                        self.send_cmd(i_, command=cmd, **vals)

                elif cmd == 'SettingChanged':
                    for i_, s_ in enumerate(self.UsrSocket):
                        if i_ != cur_gamer_index:
                            self.send_cmd(i_, command=cmd, **vals)

        self.send_all_cmd(command='EndGame')