26 / 01 / 2012 buling

闲来无事,买了个vps,学习一下装系统,先备份一些linux常用命令:
系统

# uname -a               # 查看内核/操作系统/CPU信息
# head -n 1 /etc/issue   # 查看操作系统版本
# cat /proc/cpuinfo      # 查看CPU信息
# hostname               # 查看计算机名
# lspci -tv              # 列出所有PCI设备
# lsusb -tv              # 列出所有USB设备
# lsmod                  # 列出加载的内核模块
# env                    # 查看环境变量

资源

# free -m                # 查看内存使用量和交换区使用量
# df -h                  # 查看各分区使用情况
# du -sh <目录名>        # 查看指定目录的大小
# grep MemTotal /proc/meminfo   # 查看内存总量
# grep MemFree /proc/meminfo    # 查看空闲内存量
# uptime                 # 查看系统运行时间、用户数、负载
# cat /proc/loadavg      # 查看系统负载

磁盘和分区

# mount | column -t      # 查看挂接的分区状态
# fdisk -l               # 查看所有分区
# swapon -s              # 查看所有交换分区
# hdparm -i /dev/hda     # 查看磁盘参数(仅适用于IDE设备)
# dmesg | grep IDE       # 查看启动时IDE设备

检测状况网络

# ifconfig               # 查看所有网络接口的属性
# iptables -L            # 查看防火墙设置
# route -n               # 查看路由表
# netstat -lntp          # 查看所有监听端口
# netstat -antp          # 查看所有已经建立的连接
# netstat -s             # 查看网络统计信息

进程

# ps -ef                 # 查看所有进程
# top                    # 实时显示进程状态

用户

# w                      # 查看活动用户
# id <用户名>            # 查看指定用户信息
# last                   # 查看用户登录日志
# cut -d: -f1 /etc/passwd   # 查看系统所有用户
# cut -d: -f1 /etc/group    # 查看系统所有组
# crontab -l             # 查看当前用户的计划任务

服务

# chkconfig --list       # 列出所有系统服务
# chkconfig --list | grep on    # 列出所有启动的系统服务程序
# rpm -qa                # 查看所有安装的软件包

      又是闲来无事,熟悉一下websocket相关东东,写了一个小demo,才发现不能用,最后搜索发现websocket连接有一个握手过程,这个过程和flash的鉴权是类似操作。来看看一个websocket协议的数据包结构吧,如果不喜欢看英文,可以看这篇博客的简介:http://blog.csdn.net/fenglibing/article/details/6852497:

      欲了解详情,可以访问:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10了解情况,在这个版本之前还有一个老版协议:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-02,貌似safari浏览器还用的该协议,需要了解可以直接去查看,下面贴一个websocket通信协议帮助类:

import hashlib
import base64
class WebSocket:
    def __init__(self, protocols = []):
        self.header = {}
        self.protocols = protocols
    def trim(self, str):
        '''去除空白字符'''
        rep = ['\n', '\r', ' ']
        for i in rep:
            str = str.replace(i, '')
        return str
    def unpack_data(self, data):
        '''解析数据包'''
        if self.header['Version'] == 1: #老版本
            return data[1:-1]
        code_length = ord(data[1]) & 127
        if code_length == 126:
            masks = data[4:8]
            data = data[8:]
        elif code_length == 127:
            masks = data[10:14]
            data = data[14:]
        else:
            masks = data[2:6]
            data = data[6:]
        i = 0
        raw_str = ''
        for d in data:
            raw_str += chr(ord(d) ^ ord(masks[i%4]))
            i += 1
        return raw_str
    def pack_data(self, str):
        '''打包数据'''
        if self.header['Version'] == 1: #老版本
            return '\x00'+str+'\xFF'
        data = '\x81'
        dlen = len(str)
        if dlen <= 125:
            data += chr(dlen)
        else:
            data += chr(126)
            data += chr(dlen>>8)
            data += chr(dlen&0xFF)
        return data + str
    def get_token1(self, key1, key2, key3):
        '''老版本token算法'''
        num1 = int(''.join([i for i in key1 if i.isdigit()]))
        spa1 = key1.count(' ')
        num2 = int(''.join([i for i in key2 if i.isdigit()]))
        spa2 = key2.count(' ')
        combined = struct.pack('>II', num1/spa1, num2/spa2) + key3
        return hashlib.md5(combined).digest()
    def get_hand_shake1(self):
        '''老版本头握手消息'''
        data  = 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
        data += 'Upgrade: WebSocket\r\n'
        data += 'Connection: Upgrade\r\n'
        data += 'Sec-WebSocket-Origin: %s\r\n'
        data += 'Sec-WebSocket-Location: %s\r\n'
        data += 'Sec-WebSocket-Protocol: %s\r\n\r\n'
        data = data % (self.header['Sec-WebSocket-Origin'],
                       self.header['Sec-WebSocket-Location'],
                       ','.join(self.protocols))
        return data
    def get_token2(self, key):
        '''新版本token算法'''
        key += '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        skey = hashlib.sha1(key).digest()
        return base64.b64encode(skey)
    def get_hand_shake2(self):
        '''新版本头握手消息'''
        data  = 'HTTP/1.1 101 Switching Protocols\r\n'
        data += 'Upgrade: websocket\r\n'
        data += 'Connection: Upgrade\r\n'
        data += 'Sec-WebSocket-Accept: %s\r\n'
        data += 'Sec-WebSocket-Protocol: %s\r\n\r\n'
        data = data % (self.header['Token'], ','.join(self.protocols))
        return data
    def decode_header(self, msg):
        '''解析websocket握手消息'''
        ret = msg.strip('\r\n').split('\r\n')
        for i in ret:
            h = i.split(':', 1)
            self.header[self.trim(h[0])] = self.trim(h[1])
        self.header['Sec-WebSocket-Location'] = 'ws://'+self.header['Host']
        if self.header.has_key('Sec-WebSocket-Key1'):
            self.header['Version'] = 1
            self.header['Token'] = self.get_token1(self.header['Sec-WebSocket-Key1'],
                                                   self.header['Sec-WebSocket-Key2'],
                                                   msg[-9:-1])
            return self.get_hand_shake1()
        else:
            self.header['Version'] = 2
            self.header['Token'] = self.get_token2(self.header['Sec-WebSocket-Key'])
            return self.get_hand_shake2()

这里用python的socketServer模块搭建一个socket服务来说明这个类的用法:

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    '''线程请求管理'''
    def setup(self):
        self.request.settimeout(3600) #保证连接超时释放
        self.web_socket = WebSocket(['chat', 'texas'])
    def handle(self):
        try:
            while True:
                ctime = time.time()
                buf = self.request.recv(1024)
                if not buf:
                    break
                if buf[0:20] == 'GET /socket HTTP/1.1':
                    data = self.web_socket.decode_header(buf[21:])
                    self.request.send(data)
                    print data
                else:
                    data = self.web_socket.unpack_data(buf)
                    print '%s>>%s' % (data, binascii.hexlify(buf))
                    self.request.send(self.web_socket.pack_data('server return:%s' % data))
        except socket.timeout:#连接超时
            cur_thread = threading.currentThread()
            emsg = cur_thread.getName() + ' timeout connect!'
            print emsg
        except:
            print 'error in ThreadedTCPRequestHandler :%s' % traceback.format_exc()
        print 'request closed!'
        self.request.close()
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass
class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server = None
        ThreadedTCPServer.allow_reuse_address = True
    def run(self):
        self.server = ThreadedTCPServer((self.host, self.port), ThreadedTCPRequestHandler)
        self.server.serve_forever()
if __name__ == '__main__':
    Server('0.0.0.0', 25525).run()

      前端html写作方法,没有什么特别注意的,websocket提供的api非常简单(ie9不支持,ie10支持,但需要win8才能安装,firefox支持很好,chrome支持,opera最新版不支持):




测试websocket



      最近无事,看了很多html5和css3网站相继火红。我也忍不住了,买了一本《HTML5与CSS3 权威指南》,书还是很不错,但感觉讲的有点不够升入。之前看过一本《ActionScript 3.0权威指南》,觉得写的很不错,主要是把我学了这么几年的矩阵派上用场了,神啊,买。想学习动画,但html5和css3动画教程比较少,所以打算先多学点as3,从中取点经,其实很多api都是很相通的。
      想学一下css3,一开始我以为bing的国外版本是用了css3绘制的,原来用了html5的一句video标签,大失所望,还是学习一下 http://tjrus.com/iphone 这哥们写的,来,效果:

      然后,想起淘宝网站首页都改版用html5,一下子抄的如火如荼,我想我们公司也不差这点技术呀,搞一搞也火一下呗,写了个选号的,firefox下效果最佳:

01
02
03
04
05
06
07

以前刚接触html5的时候,就想画个五角星,感觉好难。还有html5没有提供画椭圆的api,但这里有个很好的方法,画四条贝塞尔曲线,看效果:


Your browser does not support the canvas element.


Your browser does not support the canvas element.

      之前用flash写德州,ui没写完,实在有点小累,写个html5版(用canvas绘制而成的)的界面,但....本应该用css3写才是专业的,就当学习了吧,上效果(在firefox最新版本,或ie9以上查看):


Your browser does not support the canvas element.


Your browser does not support the canvas element.

无标签信息 1 条

      虽然在这次的德州demo中我只是把消息服务作为了一个线程处理,但并不影响实际的消息管理,其实也没有什么要说的,消息格式:消息长度+版本号+事件名+事件对应参数详情。之前写短信网关时候对SocketServer没有理解太深,没有设置socket的超时时间,结果导致大量请求挂起,导致最后服务挂起。
      今天一连写了三篇博客,大部分是对最近这段时间的所作描述,因为表述不怎么好,所以就贴了比较多的代码。以后再回过头来看曾经写的代码会是什么样的心情呢。在flash上我也写了一些模块,本本快没电了,现在也有点晚了,所以就放在下一篇文章中在描述吧。

#coding=gbk
import time
import struct
import socket
import logging
import traceback
import threading
import binascii
import Object
import Config
import ClientEvent
import SocketServer
import MsgPackage
import Connection
import Game.M as M
import Game.I as I
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    '''线程请求管理'''
    def setup(self):
        self.request.settimeout(Config.CLIENT_OUTTIME)
    def send(self, command, info):
        _data = MsgPackage.pack(command, info)
        _len = len(_data)
        _data = struct.pack('>L', _len) + _data
        self.request.send(_data)
        logging.info('send[%s] data[%s]:%s', M.get(command), _len, info)
    def handle(self):
        try:
            while True:
                ctime = time.time()
                buf = self.request.recv(4)
                if not buf:
                    break
                cmd_len = struct.unpack('>L', buf)[0]
                data = ''
                while len(data) < cmd_len:
                    obuf = self.request.recv(cmd_len - len(data))
                    if not obuf:
                        raise Exception('not receive anything!')
                    data += obuf
                _data = MsgPackage.unpack(data)
                logging.info('get[%s] data[%s]:%s', M.get(_data['c']), cmd_len, data)
                ret = ClientEvent.deal(_data['c'], _data['i'], self)
                if ret: #有返回消息
                    self.send(*ret)
        except socket.timeout:#连接超时
            cur_thread = threading.currentThread()
            emsg = cur_thread.getName() + ' timeout connect!'
            logging.info(emsg)
        except:
            logging.error('error in ThreadedTCPRequestHandler :%s, res:', traceback.format_exc())
        self.request.close()
        Connection.unbind(self.request)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass
class Server:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        self.server = None
        ThreadedTCPServer.allow_reuse_address = True
    def run(self):
        server_check = threading.Thread(target=self.check)
        server_check.setDaemon(True)
        server_check.start()
        self.server = ThreadedTCPServer((self.host, self.port), ThreadedTCPRequestHandler)
        self.server.serve_forever()
    def check(self):
        while not Object.END_EVENT.isSet():
            Object.END_EVENT.wait(2)
        self.shutdown()
    def stop(self):
        self.shutdown()
if __name__ == '__main__':
    Server('0.0.0.0', 25525).run()

      下面这个是上文提到的GameServer代码,主要是轮询赛桌当前应该处理的事件和自处理事件。没有什么技术含量,主要是补充上篇博客的阅读:

#coding=gbk
import time
import traceback
import logging
import threading
import Config
import Queue
import Object
_INSTANCE_ = None
class GameServer:
    def __init__(self):
        self._table_conf = {}
        self._table_msgq = {}
    def join_table(self, table):
        if self._table_conf.has_key(table.table_id):
            return -1
        self._table_conf[table.table_id] = table
        self._table_msgq[table.table_id] = Queue.Queue()
        logging.info('table[%s] created!', table.table_id)
    def do_event(self, table_id, uid, command, info):
        '''处理一个事件'''
        t_queue = self._table_msgq.get(table_id, None)
        if not t_queue:
            return -1
        t_queue.put({'uid':uid, 'event':command, 'info':info})
        return True
    def get_event(self, table_id):
        '''获取一个事件'''
        t_queue = self._table_msgq.get(table_id, None)
        if not t_queue:
            return {}
        try:
            return t_queue.get_nowait()
        except:
            return {}
    def start(self):
        t = threading.Thread(target = self.run)
        t.setDaemon(True)
        t.start()
        return self
    def run(self):
        while not Object.END_EVENT.isSet():
            try:
                for tid, table in self._table_conf.items():
                    #logging.info('%s, %s, %s', table.status, table.wait_next_uid, table.wait_end_uid)
                    if table.status != Config.GAMEOVER_STATUS:
                        table.check(self.get_event(tid))
                time.sleep(1)
            except:
                logging.error('error in GameServer.run:%s', traceback.format_exc())
def getInstance():
    '''获取游戏服务进程'''
    global _INSTANCE_
    if not _INSTANCE_:
        _INSTANCE_ = GameServer()
    return _INSTANCE_

      上周末写了一个德州牌桌demo,桌子有点大,所以我把一个桌子分成了两半,一半是处理用户请求的,包括:加注、看牌、弃牌、跟注、all in等。一半是牌桌自己事件,包括:抽取庄家位,发底牌,发翻牌,发转牌,发河牌,算奖等。其实大部分方法来说都是一两句代码,分出方法是为了维护方便,也是为了能好意思贴出来。
      先看这个牌桌这半个类吧,写了这么几年代码,总结来说:1、代码应该简洁;2、代码要有思想;3、代码逻辑性强;4、代码模块要独立。虽然我不敢说自己的代码真能做到,但我一直是往这些方面靠拢。感觉这个牌桌有这么几点自我感觉还行:1、游戏每个事件都有不同方法;2、游戏中几乎没有跟游戏无关的代码(除了事件监听和发送)。其实做事件的目的是为了通知用户赛桌的变化和对赛事包括牌谱持久化,牌谱分析统计等等。
      代码中存在一些如M、I的模块,还有Config等涉及公司机密,就不方便贴出来了,M是定义的事件类型;I是定义的消息中的参数名。Config中包括一些状态定义等等,缺少也不会影响代码阅读,自己补充一点点就能跑起来。
      先看一下,牌桌测试程序吧,这个就是常说的测试案例,有这么一个代码就好了,你可以随意修改服务,修改完成运行一次就可以知道服务有么有什么问题:

    gserv = Game.GameServer.getInstance()
    t = Game.Table.Table('id123', 6, 5)
    t.match_type = Game.Config.IMMEDIATE
    t.init_event()
    t.user_join({'u1' : [1000, 1],
                 'u2' : [1000, 2],
                 'u3' : [1000, 3],
                 'u4' : [1000, 4],
                 'u5' : [1000, 5]})
    gserv.start().join_table(t)
    n, j = 2, 1
    while not Object.END_EVENT.isSet():
        if t.wait_next_uid != 0 and n>0:
            gserv.do_event(t.table_id, t.wait_next_uid, M.CALL, {})
            n -= 1
        if t.status == 'turn' and j == 1:
            gserv.do_event(t.table_id, t.wait_next_uid, M.RAISE, {'chips':100})
            j = 0
        time.sleep(1)

      代码贴的实在是有点多了,关于GameServer我就再写一篇来描述消息接收和游戏轮询做介绍吧

#coding=gbk
import time
import logging
import M, I
import Code
import Event
import Config
import Poker
import TableUser
import EventHelper
class Table(TableUser.TableUser):
    def __init__(self, table_id, seat_num = 6, action_time = 20):
        TableUser.TableUser.__init__(self)
        self.table_id = table_id     #桌子id
        self.table_name = ''         #桌子名称
        self.table_desc = ''         #桌子简介
        self.table_poker = []        #桌上当前牌
        self.match_id = None         #牌桌所属赛事id
        self.match_type = None       #牌桌所属赛事类型
        self.seat_num = seat_num     #当前桌子座位数
        self.action_time = action_time #玩家行动超时时间
        self._poker = None           #扑克牌队列
        self.table_info = {          #牌桌相关信息
                           'bblind':100,   'sblind':50, 'ante':0, 'dealer_uid':0,
                           'bblind_uid':0, 'sblind_uid':0, 'dealer_uid':0}
        self.status_list = [Config.START_STATUS, Config.BLIND_STATUS, Config.PREFLOP_STATUS,
                            Config.FLOP_STATUS, Config.TURN_STATUS, Config.DRIVE_STATUS,
                            Config.PRIZE_STATUS, Config.GAMEOVER_STATUS]
        self.free_seat = range(1, seat_num+1) #空座位列表[座位号]
        self.pot_info = []           #奖池信息
        self.pot_uinfo = []          #奖池对应的用户信息
        self.status = Config.INIT_STATUS #初始化状态
    def init_event(self):
        '''初始化事件'''
        TableUser.TableUser.init_event(self)
        self.event.add_event_listener(Event.PREFLOP_COMPLETE, EventHelper.on_preflop_msg)
        self.event.add_event_listener(Event.FLOP_COMPLETE, EventHelper.on_flop_msg)
        self.event.add_event_listener(Event.TURN_COMPLETE, EventHelper.on_turn_msg)
        self.event.add_event_listener(Event.DRIVE_COMPLETE, EventHelper.on_drive_msg)
        self.event.add_event_listener(Event.BLIND_COMPLETE, EventHelper.on_blind_msg)
        self.event.add_event_listener(Event.READY_COMPLETE, EventHelper.on_table_ready)
        return self
    def reset(self):
        '''重置桌子信息'''
        self.table_poker = []
        self.wait_next_uid = 0
        self.user_max_chips = 0
        self._poker = Poker.get_poker_queue()
    def get_dealer_uid(self):
        '''获取庄家位子'''
        import random
        return random.choice(self.cplayer_list)
    def first_blind_uid(self):
        '''第一次设置大小盲'''
        dealer_uid = self.get_dealer_uid()
        plen = len(self.cplayer_list)
        dindex = self.cplayer_list.index(dealer_uid)
        bindex = (dindex+2) % plen
        self.table_info['sblind_uid'] = self.cplayer_list[(dindex+1) % plen]
        self.table_info['bblind_uid'] = self.cplayer_list[(dindex+2) % plen]
        self.table_info['dealer_uid'] = dealer_uid
        self.iplayer_list = self.cplayer_list[bindex+1:] + self.cplayer_list[0:bindex+1]
    def set_blind_uid(self):
        '''设置盲注相关信息'''
        lbb = self.table_info['bblind_uid']
        if not lbb: #如果是第一手
            self.first_blind_uid()
            return None
        bblind_uid = sblind_uid = 0
        if lbb in self.cplayer_list:
            sblind_uid = lbb
        else: #如果上一手的大盲离开
            sblind_uid = 0
        bindex = (self.cplayer_list.index(lbb) + 1) % len(self.cplayer_list)
        bblind_uid = self.cplayer_list[bindex]
        self.table_info['sblind_uid'] = sblind_uid
        self.table_info['bblind_uid'] = bblind_uid
        dindex = (self.cplayer_list.index(sblind_uid) - 1) % len(self.cplayer_list)
        self.table_info['dealer_uid'] = self.cplayer_list[dindex]
        self.iplayer_list = self.cplayer_list[bindex+1:] + self.cplayer_list[0:bindex+1]
    def start(self):
        '''开始游戏'''
        #检查是否可以开始比赛
        self.cplayer_list = self.get_player_list()
        if len(self.cplayer_list) < self.min_player_num:
            self.status = Config.INIT_STATUS
            return False
        logging.info('[%s] start game!', self.table_id)
        self.reset()
        self.status = Config.BLIND_STATUS
        event_content = { 'cplayer_list':self.cplayer_list,
                          'player_dict' :self.player_dict,
                          'table_id'    :self.table_id}
        self.event.dispatch_event(Event.READY_COMPLETE, content = event_content)
        return False
    def blind(self):
        '''设置大小盲注'''
        self.set_blind_uid()
        logging.info('[%s] set blind over!:%s', self.table_id, self.table_info)
        return False
    def cut_card(self):
        '''切牌'''
        ret = self._poker.get()
        logging.debug('[cut_card]:%s', ret)
        return ret
    def pre_flop(self):
        '''底牌圈'''
        self.cut_card()
        for pos in self.cplayer_list:
            self.player_dict[pos].poker_list = [self._poker.get(), self._poker.get()]
        event_content = {'cplayer_list':self.cplayer_list,
                         'player_dict' :self.player_dict,
                         'table_id'    :self.table_id}
        self.event.dispatch_event(Event.PREFLOP_COMPLETE, content = event_content)
        self.do_blind_chips() #扣除大小盲
        return True
    def flop(self):
        '''翻牌圈'''
        self.cut_card()
        self.table_poker.extend([self._poker.get(), self._poker.get(), self._poker.get()])
        event_content = {'cplayer_list':self.cplayer_list,
                         'table_poker':self.table_poker,
                         'table_id'   :self.table_id}
        self.event.dispatch_event(Event.FLOP_COMPLETE, content = event_content)
        return True
    def turn(self):
        '''转牌圈'''
        self.cut_card()
        self.table_poker.append(self._poker.get())
        event_content = {'cplayer_list':self.cplayer_list,
                         'table_poker':self.table_poker,
                         'table_id'   :self.table_id}
        self.event.dispatch_event(Event.TURN_COMPLETE, content = event_content)
        return True
    def drive(self):
        '''河牌圈'''
        self.cut_card()
        self.table_poker.append(self._poker.get())
        event_content = {'cplayer_list':self.cplayer_list,
                         'table_poker':self.table_poker,
                         'table_id'   :self.table_id}
        self.event.dispatch_event(Event.DRIVE_COMPLETE, content = event_content)
        return True
    def prize(self):
        '''派奖'''
        _uinfo = {}
        for uid in self.cplayer_list:
            _player = self.player_dict[uid]
            if _player.status != Config.FOLD:
                _uinfo[uid] = _player.poker_list + self.table_poker
        ret = None
        if len(_uinfo) > 1:
            ret = Poker.compare(_uinfo)
        print _uinfo
        logging.info('[%s] prize :%s', self.table_id, ret)
        return False
    def game_over(self):
        '''游戏结束'''
        for i in self.cplayer_list:
            player = self.player_dict[i]
            player.poker_list = []
        logging.info('game_over!')
        return False
    def _do_blind_chips(self, type, uchips = None):
        '''扣除盲注'''
        player = self.player_dict[self.table_info[type+'_uid']]
        bchips = uchips and uchips or self.table_info[type]
        info = {'chips':bchips}
        self.user_raise(self.table_info[type+'_uid'], info)
        return bchips
    def do_blind_chips(self):
        '''扣除大小盲注'''
        schips = self._do_blind_chips('sblind') #扣除小盲注
        splayer = self.player_dict[self.table_info['sblind_uid']]
        #当前只有大小盲,且小盲all in
        if len(self.cplayer_list) == 2 and splayer.status == Config.ALL_IN:
            bchips = self._do_blind_chips('bblind', schips) #扣除大盲注
        else:
            bchips = self._do_blind_chips('bblind') #扣除大盲注
        event_content = {'cplayer_list':self.cplayer_list,
                         'table_info'  :self.table_info,
                         'blind_info'  :{self.table_info['bblind_uid']:bchips,
                                         self.table_info['sblind_uid']:schips},
                         'table_id'    :self.table_id}
        self.event.dispatch_event(Event.BLIND_COMPLETE, content = event_content)
    def check(self, data = {}):
        '''服务检查'''
        if self.status == Config.INIT_STATUS: #初始态
            return None
        if data.get('uid', None): #用户有请求
            self.player_bet(data['uid'], data['event'], data['info'])
        if self.wait_next_uid == 0 : #做游戏事情
            if self._check_game_over():
                self.status = Config.PRIZE_STATUS
            if not hasattr(self, self.status):
                self.error('error status attr:%s', self.status)
                return None
            if getattr(self, self.status)():
                self._turn_player(self._get_wait_next())
            self.status = self._get_next_status() #跳转到下一状态
        else: #判断用户状态
            player = self.player_dict[self.wait_next_uid]
            if time.time() > player.out_time: #超时
                player.status = Config.NURSE
            if player.status == Config.NURSE: #托管中
                self.user_nurse(self.wait_next_uid)
    def error(self, msg = ''):
        logging.info('error:%s', msg)
    ####################################################################################
    def _check_game_over(self):
        '''判断游戏是否完成'''
        if len(self.iplayer_list) - self.fold_unum == 1: #只剩下一个用户
            return True
        return False
    def _get_next_status(self):
        '''获取下一个游戏状态'''
        if self.status == self.status_list[-1]:
            return self.status
        sindex = self.status_list.index(self.status)
        return self.status_list[sindex + 1]

      下面在来看一下,上面类中继承的TableUser.py模块,该模块主要为了响应用户事件,包括用户超时自动设为托管状态等。

#coding=gbk
import time
import logging
import M, I
import Code
import Event
import Player
import Config
import EventHelper
class TableUser:
    '''桌上玩家动作'''
    def __init__(self):
        self.player_dict = {}        #当前牌桌用户列表 {玩家uid:player}
        self.cplayer_list = []       #当前玩家列表[玩家uid]
        self.nplayer_list = []       #新加入玩家list[玩家uid]
        self.iplayer_list = []       #当前说话玩家顺序
        self.fold_unum = 0           #弃牌用户数
        self.wait_next_uid = 0       #等待的出价玩家uid
        self.user_max_chips = 0      #用户最大出价
        self.min_player_num = 2      #最小玩家数
        self.free_seat = []          #空座位列表[座位号]
        self.event = Event.Event()   #相关事件句柄
        self.user_method_conf = {M.CALL:self.user_call, M.RAISE:self.user_raise,
                                 M.FOLD:self.user_fold, M.CHECK:self.user_check,
                                 M.ALL_IN:self.user_all_in}
    def init_event(self):
        '''初始化事件'''
        self.event.add_event_listener(Event.USER_JOIN, EventHelper.on_user_join)
    def get_player_list(self):
        '''初始化当前玩家列表'''
        _cplayer_list = []
        for uid, player in self.player_dict.items():
            if player.chips > 0 : #取出有筹码的用户
                player.unplay_times = 0
                _cplayer_list.append(uid)
            else:
                player.unplay_times += 1
        if self.cplayer_list: #不是第一手,就有新手列表
            self.nplayer_list = set(_cplayer_list) - set(self.cplayer_list)
        _cplayer_list.sort(cmp=lambda x,y:self.player_dict[x].seat_pos-self.player_dict[y].seat_pos)
        return _cplayer_list
    def _user_join(self, uid, chips=0, seat_pos=None):
        '''玩家加入牌桌'''
        if not self.free_seat: #没有空座
            return Code.NOFREE_SEAT
        if not seat_pos: #分配座位号
            seat_pos = self.free_seat[0]
        elif seat_pos not in self.free_seat: #座位已经被占用
            return Code.EXIST_PLAYER
        player = Player.Player(uid, chips, Config.OK)
        player.seat_pos = seat_pos
        self.player_dict[uid] = player
        self.free_seat.remove(seat_pos)
        event_content = {'info'        :player,
                         'player_dict':self.player_dict,
                         'player_dict' :self.player_dict,
                         'table_id'    :self.table_id}
        self.event.dispatch_event(Event.USER_JOIN, content = event_content)
        return Code.OK
    def user_join(self, userdict = {'uid':['chips', 'seat_pos']}):
        '''用户加入牌桌'''
        code = Code.OK
        for uid, uinfo in userdict.items():
            code = self._user_join(uid, uinfo[0], uinfo[1])
        if self.status == Config.INIT_STATUS and self.match_type == Config.IMMEDIATE:
            self.start() #开始比赛
        return code
    def player_bet(self, uid, event, info):
        '''玩家下注'''
        if self.wait_next_uid != uid:
            return Code.NOTURN_YOU #还没轮到该用户
        if uid not in self.cplayer_list:
            return Code.NOPLAYER #用户非法
        if self.player_dict[uid].status in [Config.FOLD, Config.ALL_IN]:
            return Code.NOPRIM_DO #没有说话权利
        if not self.user_method_conf.has_key(event):
            return Code.DO_ERROR #没有该玩家事件
        ret = self.user_method_conf[event](uid, info)
        if ret is not True:
            return ret
        self.check_game(uid)
        return True
    def check_game(self, uid):
        '''做系统判断'''
        if self._check_street(uid):
            self._reset_street() #重置这一街情况
            self.wait_next_uid = 0
            return None
        next_uid = self._get_wait_next()
        self._turn_player(next_uid)
    def user_call(self, uid, info = None):
        '''玩家跟注'''
        player = self.player_dict[uid]
        uchip = sum(player.bid_list)
        ichips = 0
        if player.chips+uchip > self.user_max_chips:
            ichips = self.user_max_chips - uchip
            player.chips -= ichips
            player.bid_list.append(ichips)
            logging.info('[%s][%s] call :%s', self.table_id, uid, ichips)
        else: #筹码不够,all in
           self.user_all_in(uid)
        return True
    def user_raise(self, uid, info = None):
        '''玩家加注'''
        player = self.player_dict[uid]
        chips = info.get('chips')
        if player.chips > chips:
            player.chips -= chips
            player.bid_list.append(chips)
            self.user_max_chips = max(chips, self.user_max_chips)
            logging.info('[%s][%s] raise :%s', self.table_id, uid, chips)
        else: #筹码不够,all in
            self.user_all_in(uid)
        return True
    def user_fold(self, uid, info = None):
        '''玩家弃牌'''
        self.player_dict[uid].status = Config.FOLD
        self.fold_unum += 1
        logging.info('[%s][%s] fold!', self.table_id, uid)
        return True
    def user_check(self, uid, info = None):
        '''玩家看牌'''
        player = self.player_dict[uid]
        uchip = sum(player.bid_list)
        print 'user_check test[%s, %s, %s]' % (uid, uchip, self.user_max_chips)
        if uchip >= self.user_max_chips:
            logging.info('[%s][%s] check!', self.table_id, uid)
            return True
        return Code.NOPRIM_CHECK #没有权限看牌
    def user_all_in(self, uid, info = None):
        '''玩家ALL IN'''
        player = self.player_dict[uid]
        chips = player.chips
        player.chips = 0
        player.status = Config.ALL_IN
        player.bid_list.append(chips)
        self.user_max_chips = max(chips, self.user_max_chips)
        logging.info('[%s][%s] all in :%s', self.table_id, uid, chips)
        return True
    def user_nurse(self, uid):
        '''玩家智能托管'''
        player = self.player_dict[uid]
        if self.user_check(uid) is not True: #看牌失败
            self.user_fold(uid)
        self.check_game(uid)
####################################################################
    def _turn_player(self, uid):
        '''轮到某玩家说话'''
        logging.info('status:%s, wait:%s', self.status, uid)
        self.wait_next_uid = uid
        if uid == 0:
            return uid
        player = self.player_dict[uid]
        player.out_time = time.time()+self.action_time
    def _reset_street(self):
        '''一街完成重置'''
        self.user_max_chips = 0
        uchips = 0 #统计底池
        for i in self.cplayer_list:
            player = self.player_dict[i]
            uchips += sum(player.bid_list)
            player.bid_list = []
        self.pot_info.append(uchips)
    def _check_street(self, uid):
        '''检查一街是否完成'''
        if uid != self.iplayer_list[-1]:
            return False
        for i in self.cplayer_list:
            player = self.player_dict[i]
            if player.status in [Config.FOLD, Config.ALL_IN]:
                continue
            if sum(player.bid_list) < self.user_max_chips:
                return False
        return True
    def _get_wait_next(self):
        '''设置当前轮到下一玩家'''
        index = 0
        if self.wait_next_uid:
            index = (self.iplayer_list.index(self.wait_next_uid)+1) % len(self.iplayer_list)
        next_uid = 0
        print 'iplayer_list:%s, index:%s, nuid:%s ' % (self.iplayer_list, index, self.wait_next_uid)
        for uid in self.iplayer_list[index:]+self.iplayer_list[0:index]:
            player = self.player_dict[uid]
            if player.status not in [Config.FOLD, Config.ALL_IN]:
                next_uid = uid
                break
        if self.wait_next_uid == next_uid:
            next_uid = 0
        return next_uid

      上次说到元旦的时候coding了一个德州的服务,这段时间刚好插不上什么手就调试了一下服务的通信问题。到目前这个服务都是一个进程,分三个块:1、消息连接管理线程(负责接收客户端消息,并做调度);2、赛事管理线程(目前还没有独立成线程,还没有做到锦标赛一步);3、赛桌管理线程(主要负责管理当前开启的赛桌正常运行)。
      为了跟客户端代码靠齐,我diy了一个python版event模块。其实就是以前写的插件方法上做了一点修饰。先看用法吧:

self.event = Event.Event()   #相关事件句柄
# 监听用户加入牌桌事件,EventHelper是事件方法集合模块
self.event.add_event_listener(Event.USER_JOIN, EventHelper.on_user_join)
#在处理完用户加入牌桌后加上如下代码:
event_content = {'uid':123, 'seat_pos':2}
self.event.dispatch_event(Event.USER_JOIN, content = event_content)

      下面这个是Event.py的全部代码,写的不怎么的,就凑合着看吧,稍后我把游戏逻辑Table.py代码贴出来,基于状态,虽然这个想法我是听来的,我也没有找到更好的方法。

#coding=gbk
import logging
import traceback
READY_COMPLETE        = 'ready_over'         #准备就绪
BLIND_COMPLETE        = 'blind_over'         #扣除盲注完
PREFLOP_COMPLETE      = 'send_preflop_over'  #发底牌
FLOP_COMPLETE         = 'send_flop_over'     #发翻牌完
TURN_COMPLETE         = 'send_turn_over'     #发转牌完
DRIVE_COMPLETE        = 'send_drive_over'    #发河牌完
PRIZE_COMPLETE        = 'prize_over'         #派奖完成
USER_JOIN             = 'user_join'          #用户加入
USER_TURN             = 'user_turn'          #轮转用户
USER_CHECK            = 'user_check'         #玩家看牌
USER_CALL             = 'user_call'          #玩家跟注
USER_RAISE            = 'user_raise'         #玩家加注
USER_FOLD             = 'user_fold'          #玩家弃牌
USER_ALLIN            = 'user_allin'         #玩家all in
class Event:
    '''事件侦听和派发模块'''
    def __init__(self):
        self._event_config = {}
    def _get_unique_id(self, target, function, priority):
        return '%s_%s_%s' % (target, function.__hash__(), priority)
    def add_event_listener(self, target, function, priority=10):
        '''添加事件监听'''
        idx = self._get_unique_id(target, function, priority)
        if not self._event_config.has_key(target):
            self._event_config[target] = {}
        if not self._event_config[target].has_key(priority):
            self._event_config[target][priority] = {}
        self._event_config[target][priority][idx] = {'function' : function}
    def dispatch_event(self, target, content = None, *args, **kwargs):
        '''分发事件'''
        if not self._event_config.has_key(target):
            return None
        actions = self._event_config[target]
        prikey = actions.keys()
        prikey.sort() #按优先级排序
        for k in prikey:
            acs = actions[k]
            for ac in acs:
                a = acs[ac]['function']
                try:
                    a(content, *args, **kwargs)
                except:
                    logging.error('some error in %s-do_action:%s', target, traceback.format_exc())
无标签信息 0 条

      今天在调服务的时候老是发现两个线程读同一模块内存的时候出现问题,情况是一个线程负责写,但另一个线程始终读不到数据,最后做测试的时候发现是这么回事,先看这个小例子吧。
      当前目录下有一个haha的包,包中包含:__init__.py、data.py,data.py文件内容如下:

CONF = {}

      在当前目录下的主程序文件test.py中写下如下代码:

sys.path.append('./haha')
import server as hs
import haha.server as hs1
from haha import server as hs2
hs.CONF['hs'] = 1
hs1.CONF['hs1'] = 1
hs2.CONF['hs2'] = 1
print hs, '\n', hs1, '\n', hs2, '\n'
#输出:
#      
#      
print hs.CONF, '\n', hs1.CONF, '\n', hs2.CONF
#输出:{'hs': 1}
#      {'hs2': 1, 'hs1': 1}
#      {'hs2': 1, 'hs1': 1}

      看输出应该能知道个大概了吧,虽然还没有找到具体原因是什么,主要是不知道这种东西应该怎么搜索。xing哥猜测是不同module name用了不同的内存空间,我也同意这个解释。

      先介绍一副写扑克牌的方法吧,一般的方法莫过于随机生成牌,检查牌是否存在,在循环,这种方法是普通青年的方法,极不可靠。下面这个算是文艺青年方法吧,有内涵而且只会循环一次就写完扑克牌。备注:0、1、2、3 = 黑、红、梅、方,如果你不喜欢换换也无所谓;11、12、13、14 = J、Q、K、A。为什么A是14呢,主要是为了算牌的方便。

import random
import math
def get_poker():
    '''获取一副打乱的扑克牌'''
    ret = [
        (0, 14),(0, 2),(0, 3),(0, 4),(0, 5),(0, 6),(0, 7),(0, 8),(0, 9),(0, 10),(0, 11),(0, 12),(0, 13),
        (1, 14),(1, 2),(1, 3),(1, 4),(1, 5),(1, 6),(1, 7),(1, 8),(1, 9),(1, 10),(1, 11),(1, 12),(1, 13),
        (2, 14),(2, 2),(2, 3),(2, 4),(2, 5),(2, 6),(2, 7),(2, 8),(2, 9),(2, 10),(2, 11),(2, 12),(2, 13),
        (3, 14),(3, 2),(3, 3),(3, 4),(3, 5),(3, 6),(3, 7),(3, 8),(3, 9),(3, 10),(3, 11),(3, 12),(3, 13) ]
    #默认0是黑、1是红、2是梅、3是方
    for i in range(52) :
        k = int(math.floor(52*random.random()))
        b = ret[k] #这几句特没技术含量⊙﹏⊙
        ret[k] = ret[i]
        ret[i] = b
    return ret

      下面说贴一个今天写的算牌类吧,因为不是在公司写的,所以就提供出来,方便大家使用,只写了一遍没有做细检查,如果有bug麻烦贴一下,先说比牌的方法吧。

def compare(cards_dict):
    '''比较牌大小'''
    rank = 0
    ret = {}
    #先取出最大同等级的所有牌谱,比如都是同花顺
    for id, plist in cards_dict.items():
        res = CardCounter(plist).result()
        if res['type'] > rank:
            rank = res['type']
            ret = {id:res}
        elif res['type'] == rank:
            ret[id] = res
    iret = {}
    max = None
    #同等级的所有牌谱比较,比如都是同花顺,比较最大牌
    for id, res in ret.items():
        if not max or res['result'] > max:
            max = res['result']
            iret = {id: res}
        elif res['result'] == max:
            iret[id] = res
    return iret

      使用这个方法比较几副扑克牌,以下面的比较9个人的扑克牌,执行1000次耗时0.5秒左右,下面演示一下:

    poker = get_poker #得到一副扑克
    u_list = {}
    cpoker = poker[0:5] #取出前5张作为公共牌
    num = 6
    for i in range(9): #生成9个玩家牌
        po = cpoker + poker[num:num+2]
        num += 3
        u_list['test_%s' % i] = po
        print 'test_%s  =>  %s' % (i, po)
    res = compare(u_list) #比较扑克,得到最大玩家
    print res

      上面的代码中出现了一个关键的类CardCounter,现贴一下代码:

import copy
SUITED_JUNKO   = 10 #同花顺
QUADS          = 9  #四条
FULL_HOUSE     = 8  #葫芦
SUITED         = 7  #同花
JUNKO          = 6  #顺子
TRIPS          = 5  #三条
TWO_PAIRS      = 4  #两对
PAIR           = 3  #一对
HIGH           = 2  #高牌
# CardCounter的使用方法如下:
# upoker = {'uname1':[(0,14), (0,2), (0,3), (0,4), (0,5), (0,9), (0,8)]
#           'uname2':[(0,14), (0,2), (0,3), (0,4), (0,5), (0,6), (0,8)]}
# ret = CardCounter(upoker).result()
# ret返回的是个字典,解释一下字典中的项:
# {'type':'',   #牌型,上面定义的常量,如同花、顺子
#  'card':'',   #在四条中和一对中表示牌点数
#  'tcard':'',  #在三条和葫芦中表示三条牌点数
#  'pcard':',   #在葫芦中表示一对的点数
#  'cards':'',  #最大5张中的其余牌
#  'color':'',  #在同花顺和同花中表示花色
#  'result':''  #同等级牌的比较项
# 各字段只在描述的牌型中出现
class CardCounter:
    '''德州扑克牌计算类'''
    def __init__(self, poker_list):
        self._plist = poker_list
        self._clist = [] #花色列表
        self._nlist = [] #点数列表
        self._olist = [] #不同点数列表
        self.init_info()
    def init_info(self):
        '''初始化相关信息'''
        self._clist = [i[0] for i in self._plist]
        self._nlist = [i[1] for i in self._plist]
        self._nlist.sort()
        self._olist = list(set(self._nlist))
        self._olist.sort()
    def get_junko(self, olist):
        '''获取顺子'''
        n = len(olist)
        if n<5:
            return False
        for i in range(n-5, -1, -1):
            xlist = olist[i:5+i]
            if xlist[-1] - xlist[0] == 4:
                return xlist
        return set([14, 2, 3, 4, 5]) < set(olist) and [14, 2, 3, 4, 5] or False
    def get_suited(self, clist, plist):
        '''是否是同花'''
        flag = False
        for i in (0, 1, 2, 3):
            if clist.count(i)>= 5:
                flag = i
        if flag is False:
            return flag
        plist = [i[1] for i in plist if i[0] == flag]
        plist.sort()
        return (flag, plist)
    def get_pairs(self, olist, nlist):
        '''获取对'''
        ret = []
        for i in olist:
            if nlist.count(i) >= 2 :
                ret.append(i)
        ret.sort()
        return ret
    def get_trips(self, olist, nlist):
        '''获取三条'''
        ret = []
        for i in olist:
            if nlist.count(i) == 3 :
                ret.append(i)
        ret.sort()
        return ret
    def get_quads(self, olist, nlist):
        '''获取四条'''
        for i in olist:
            if nlist.count(i) == 4 :
                return i
        return False
    def result(self):
        '''获取牌型的最大牌'''
        suited = self.get_suited(self._clist, self._plist)
        if suited:
            color, xsuited = suited
            suited_junko = self.get_junko(xsuited)
            if suited_junko: #同花顺
                return {'type':SUITED_JUNKO, 'cards':suited_junko[-1:-6:-1],
                        'color':color, 'result':suited_junko[-1]}
        n = len(self._olist)
        if n<=4:
            quads = self.get_quads(self._olist, self._nlist)
            if quads: #4条
                olist = copy.copy(self._olist)
                olist.remove(quads)
                return {'type':QUADS, 'card':quads, 'cards':olist[-1:-2:-1], 'result':olist[-1]}
        pairs = self.get_pairs(self._olist, self._nlist)
        trips = self.get_trips(self._olist, self._nlist)
        if len(pairs) >= 2 and trips:
            for i in trips[::-1]:
                if i in pairs: #葫芦
                    pcard = pairs[-1] == i and pairs[-2] or pairs[-1]
                    return {'type':FULL_HOUSE, 'tcard':i, 'pcard':pcard,
                            'result':[i, pcard]}
        if suited: #同花
            color, xsuited = suited
            return {'type':SUITED, 'cards':xsuited[-1:-6:-1], 'color':color,
                    'result':xsuited[-1:-6:-1]}
        junko = self.get_junko(self._olist)
        if junko: #顺子
            return {'type':JUNKO, 'cards':junko,
                    'result':junko[-1]}
        if trips: #三条
            olist = copy.copy(self._olist)
            olist.remove(trips[-1])
            return {'type':TRIPS, 'tcard':trips[-1], 'cards':olist[-1:-3:-1],
                    'result':[trips[-1], olist[-1:-3:-1]]}
        if len(pairs) >= 2: #两对
            olist = copy.copy(self._olist)
            olist.remove(pairs[-1])
            olist.remove(pairs[-2])
            return {'type':TWO_PAIRS, 'tcard':pairs[-1:-3:-1], 'cards':olist[-1:-2:-1],
                    'result':[pairs[-1:-3:-1], olist[-1:-2:-1]] }
        if pairs: #一对
            olist = copy.copy(self._olist)
            olist.remove(pairs[0])
            return {'type':PAIR, 'card':pairs[0], 'cards':olist[-1:-4:-1],
                    'result':[pairs[0], olist[-1:-4:-1]] }
        return {'type':HIGH, 'cards':self._olist[-1:-6:-1],
                'result':self._olist[-1:-6:-1]}

      熬了N久终于等到了一个元旦,第一天睡了个很懒的觉,下午看了会下午电视,晚上和hong、rongrong一起去看《金陵十三钗》,我终于知道为什么mtime上的评价那么极端了,一帮人10分,一帮人0分,不知道是不是没什么爱心还是这个社会把我伤着了,没怎么被感动。第二天早上6点就醒来,感觉好久没写代码了,7点起床写代码,这一写就是一天,晚上和他们去吃了巴西烤肉,一群俗人吃撑了,走着回家,还有个铅笔青年一路上说要吐。第三天(今天)在家接着coding了一天。现公布成绩。
      昨天写了游戏的大框架,实在不是一个一天就能完成的东西,到现在还是个半途而废的东西呢。明天又要上班了,怎么可以把我从服务中解脱出来呢。啊啊啊啊…..