├── .gitignore ├── README.md ├── client.go ├── client2.go ├── global └── table.go ├── handlers ├── action_select.go ├── create_room.go ├── discard.go ├── enter_room.go ├── heartbeat.go ├── identity.go └── ready.go ├── machine ├── action.go ├── config.go ├── define.go ├── player.go ├── player_machine.go ├── player_manager.go ├── player_rule_state.go ├── player_rules.go ├── player_state.go ├── table.go ├── table_machine.go ├── table_manager.go ├── table_rules.go ├── table_state.go └── win_algorithm.go ├── proto ├── proto_func.go ├── server_proto.pb.go └── server_proto.proto ├── server.go └── teleport ├── README.md ├── client.go ├── conn.go ├── debug └── debug.go ├── netdata.go ├── protocol.go ├── return_func.go ├── server.go ├── teleport.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 有限状态机 2 | ===== 3 | 4 | 基于长连接游戏设计的状态机(以棋牌游戏--麻将为例) 5 | 6 | ------ 7 | 8 | ### 1. 三方库 9 | teleport :https://github.com/henrylee2cn/teleport/raw/master/doc/Teleport 10 | > 本项目使用了teleport作为长连接交互的底层实现,但稍微做了一些修改: 11 | > 1. 将原有心跳、认证等hander提取出来,与其他请求handler合并成handler包 12 | > 2. 将原内部请求的请求体改为protobuf的字节流,所有协议使用protobuf3 13 | 14 | 15 | ### 2. 代码结构 16 | server 17 | │ 18 | ├─global 一些全局变量 19 | ├─handlers 请求的handler 20 | ├─machine 状态机代码 21 | │ ├─action.go 玩家行为的结构体 22 | │ ├─config.go 房间(桌子)配置 23 | │ ├─define.go 行为、状态、事件、操作、卡牌定义 24 | │ ├─player.go 玩家对象 25 | │ ├─player_machine.go 玩家状态机 26 | │ ├─player_manager.go 玩家行为管理器 27 | │ ├─player_rules.go 玩家的检验规则 28 | │ ├─player_rule_state.go 玩家触发规则时进入的状态 29 | │ ├─player_state.go 玩家正常流程规则 30 | │ ├─table.go 房间(桌子)对象 31 | │ ├─table_machine.go 房间(桌子)状态机 32 | │ ├─table_manager.go 房间(桌子)行为管理器 33 | │ ├─table_rules.go 房间(桌子)的检验规则 34 | │ ├─table_state.go 房间(桌子)的正常流程 35 | │ └─win_algorithm.go 胡牌的DFS算法 36 | ├─proto protobuf3的协议代码 37 | └─teleport 长连接交互底层 38 | 39 | ### 3. 说明 40 | 使用teleport使本人在初期注重逻辑开发的时候节约了大量时间,但个人感觉teleport不适合跨平台交互,因此后面一些工作将会着重于长连接底层的改写。 41 | 另外,目前这套代码仅仅实现了主要逻辑部分,重连、数据落盘、日志整理等都在计划当中。keep coding... 42 | 43 | ### 4. coding... -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./teleport" 5 | "./teleport/debug" 6 | "./handlers" 7 | "log" 8 | //"time" 9 | "github.com/golang/protobuf/proto" 10 | "./proto" 11 | "bufio" 12 | "os" 13 | "strconv" 14 | "fmt" 15 | ) 16 | 17 | var table_id string 18 | 19 | // 有标识符UID的demo,保证了客户端链接唯一性 20 | func main() { 21 | 22 | debug.Debug = true 23 | 24 | uid := "C1" 25 | 26 | //注册请求处理函数 27 | clientHandlers := teleport.API{ 28 | "CreateRoomReturn" : new(ClientHeartBeat), 29 | "ActionResponse" : new(ActionResponse), 30 | "ActionPrompt" : new(ActionPrompt), 31 | //teleport.HEARTBEAT : new(ClientHeartBeat), 32 | teleport.IDENTITY : new(handlers.Identity), 33 | } 34 | 35 | //启动客户端 36 | tp := teleport.New().SetUID(uid, "abc").SetAPI( clientHandlers ) 37 | tp.Client("127.0.0.1", ":20125") 38 | 39 | req := &server_proto.CreateRoomRequest{uid,4} 40 | data, err := proto.Marshal(req) 41 | if err != nil { 42 | log.Fatal("create room request error: ", err) 43 | } 44 | 45 | tp.Request(data, "CreateRoom", "create_room_flag") 46 | 47 | //指令 48 | running := true 49 | var inp []byte 50 | var data2 []byte 51 | var order string 52 | for running { 53 | fmt.Println("please input :") 54 | reader := bufio.NewReader(os.Stdin) 55 | inp, _, _ = reader.ReadLine() 56 | order = string(inp) 57 | if order == "stop"{ 58 | running = false 59 | } else if order == "discard" { 60 | inp, _, _ = reader.ReadLine() 61 | card_input := string(inp) 62 | card, _ := strconv.Atoi(card_input) 63 | request := &server_proto.DiscardRequest{ 64 | int32(card), 65 | } 66 | data2 = server_proto.MessageEncode( request ) 67 | tp.Request(data2, "Discard", "discard_flag") 68 | } else if order == "action" { 69 | inp, _, _ = reader.ReadLine() 70 | select_id_input := string(inp) 71 | select_id, _ := strconv.Atoi(select_id_input) 72 | request := &server_proto.ActionSelectRequest{ 73 | int32(select_id), 74 | } 75 | data2 = server_proto.MessageEncode( request ) 76 | tp.Request(data2, "ActionSelect", "action_select") 77 | } else if order == "ready" { 78 | tp.Request(nil, "Ready", "ready_flag") 79 | } 80 | } 81 | 82 | select {} 83 | } 84 | 85 | type ClientHeartBeat struct{} 86 | func (*ClientHeartBeat) Process(receive *teleport.NetData) *teleport.NetData { 87 | 88 | log.Println("=============room create return===============") 89 | 90 | // 进行解码 91 | response := &server_proto.CreateRoomResponse{} 92 | server_proto.MessageDecode( receive.Body, response ) 93 | log.Println(response.RoomId) 94 | 95 | request := &server_proto.EnterRoomRequest{response.RoomId} 96 | data := server_proto.MessageEncode(request) 97 | 98 | return teleport.ReturnData( data, "EnterRoom" ) 99 | } 100 | 101 | type ActionResponse struct{} 102 | func (*ActionResponse) Process(receive *teleport.NetData) *teleport.NetData { 103 | 104 | log.Println("=============ActionResponse===============") 105 | 106 | // 进行解码 107 | response := &server_proto.ActionResponse{} 108 | server_proto.MessageDecode( receive.Body, response ) 109 | log.Println(response.Uuid," ",response.ActionName," ",response.Card) 110 | return nil 111 | } 112 | 113 | type ActionPrompt struct {} 114 | func (*ActionPrompt) Process(receive *teleport.NetData) *teleport.NetData { 115 | 116 | log.Println("=============ActionPrompt===============") 117 | 118 | // 进行解码 119 | response := &server_proto.ActionPrompt{} 120 | server_proto.MessageDecode( receive.Body, response ) 121 | for _,v := range response.Action { 122 | log.Println( v ) 123 | } 124 | return nil 125 | } -------------------------------------------------------------------------------- /client2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./teleport" 5 | "./teleport/debug" 6 | "./handlers" 7 | "./proto" 8 | "fmt" 9 | "bufio" 10 | "os" 11 | "strconv" 12 | "log" 13 | ) 14 | 15 | // 有标识符UID的demo,保证了客户端链接唯一性 16 | func main() { 17 | 18 | debug.Debug = true 19 | 20 | uid := "C2" 21 | room_id := int32(111111) 22 | 23 | //注册请求处理函数 24 | clientHandlers := teleport.API{ 25 | "ActionResponse" : new(ActionResponse2), 26 | "ActionPrompt" : new(ActionPrompt2), 27 | teleport.IDENTITY : new(handlers.Identity), 28 | } 29 | 30 | //启动客户端 31 | tp := teleport.New().SetUID(uid, "abc").SetAPI( clientHandlers ) 32 | tp.Client("127.0.0.1", ":20125") 33 | 34 | request := &server_proto.EnterRoomRequest{room_id} 35 | data := server_proto.MessageEncode(request) 36 | 37 | tp.Request(data, "EnterRoom", "enter_room_flag") 38 | 39 | //指令 40 | running := true 41 | var inp []byte 42 | var data2 []byte 43 | var order string 44 | for running { 45 | fmt.Println("please input :") 46 | reader := bufio.NewReader(os.Stdin) 47 | inp, _, _ = reader.ReadLine() 48 | order = string(inp) 49 | if order == "stop"{ 50 | running = false 51 | } else if order == "discard" { 52 | inp, _, _ = reader.ReadLine() 53 | card_input := string(inp) 54 | card, _ := strconv.Atoi(card_input) 55 | request := &server_proto.DiscardRequest{ 56 | int32(card), 57 | } 58 | data2 = server_proto.MessageEncode( request ) 59 | tp.Request(data2, "Discard", "discard_flag") 60 | } else if order == "action" { 61 | inp, _, _ = reader.ReadLine() 62 | select_id_input := string(inp) 63 | select_id, _ := strconv.Atoi(select_id_input) 64 | request := &server_proto.ActionSelectRequest{ 65 | int32(select_id), 66 | } 67 | data2 = server_proto.MessageEncode( request ) 68 | tp.Request(data2, "ActionSelect", "action_select") 69 | } else if order == "ready" { 70 | tp.Request(nil, "Ready", "ready_flag") 71 | } 72 | } 73 | 74 | select {} 75 | } 76 | 77 | type ActionResponse2 struct{} 78 | func (*ActionResponse2) Process(receive *teleport.NetData) *teleport.NetData { 79 | 80 | log.Println("=============ActionResponse===============") 81 | 82 | // 进行解码 83 | response := &server_proto.ActionResponse{} 84 | server_proto.MessageDecode( receive.Body, response ) 85 | log.Println(response.Uuid," ",response.ActionName," ",response.Card) 86 | return nil 87 | } 88 | 89 | type ActionPrompt2 struct {} 90 | func (*ActionPrompt2) Process(receive *teleport.NetData) *teleport.NetData { 91 | 92 | log.Println("=============ActionPrompt===============") 93 | 94 | // 进行解码 95 | response := &server_proto.ActionPrompt{} 96 | server_proto.MessageDecode( receive.Body, response ) 97 | for _,v := range response.Action { 98 | log.Println( v ) 99 | } 100 | return nil 101 | } -------------------------------------------------------------------------------- /global/table.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "../teleport" 5 | ) 6 | 7 | 8 | var SERVER = teleport.New() -------------------------------------------------------------------------------- /handlers/action_select.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | "../machine" 7 | ) 8 | 9 | type ActionSelect struct {} 10 | 11 | func (*ActionSelect) Process(receive *teleport.NetData) *teleport.NetData { 12 | 13 | log.Println("handler---------action select------------") 14 | 15 | uid := receive.From 16 | 17 | //用户存在 18 | if player,ok := machine.GLOBAL_USER[uid]; ok { 19 | player.Machine.CurrentState.Execute( player, machine.PlayerEvent["PLAYER_EVENT_ACTION"], receive.Body ) 20 | } else { 21 | log.Printf("找不到用户") 22 | } 23 | 24 | return nil 25 | } -------------------------------------------------------------------------------- /handlers/create_room.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | "../machine" 7 | "../proto" 8 | ) 9 | 10 | type CreateRoom struct {} 11 | 12 | func (*CreateRoom) Process(receive *teleport.NetData) *teleport.NetData { 13 | 14 | log.Println("---------create room------------") 15 | 16 | // 进行解码 17 | request := &server_proto.CreateRoomRequest{} 18 | server_proto.MessageDecode( receive.Body, request ) 19 | log.Println(request.Uuid) 20 | log.Println(request.Round) 21 | 22 | 23 | table := machine.CreateTable( receive.From ) 24 | new_machine := machine.NewTableMachine( &table, nil, nil ) 25 | table.Machine = &new_machine 26 | machine.GLOBAL_TABLE[table.TableId] = &table 27 | 28 | //返回 29 | response := &server_proto.CreateRoomResponse{int32(table.TableId)} 30 | data := server_proto.MessageEncode( response ) 31 | 32 | return teleport.ReturnData(data,"CreateRoomReturn") 33 | } -------------------------------------------------------------------------------- /handlers/discard.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | "../machine" 7 | ) 8 | 9 | type Discard struct {} 10 | 11 | func (*Discard) Process(receive *teleport.NetData) *teleport.NetData { 12 | 13 | log.Println("handler---------discard------------") 14 | 15 | uid := receive.From 16 | 17 | //用户存在 18 | if player,ok := machine.GLOBAL_USER[uid]; ok { 19 | player.Machine.CurrentState.Execute( player, machine.PlayerEvent["PLAYER_EVENT_DISCARD"], receive.Body ) 20 | } else { 21 | log.Printf("找不到用户") 22 | } 23 | 24 | return nil 25 | } -------------------------------------------------------------------------------- /handlers/enter_room.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | "../proto" 7 | "github.com/golang/protobuf/proto" 8 | "../machine" 9 | ) 10 | 11 | type EnterRoom struct {} 12 | 13 | func (*EnterRoom) Process(receive *teleport.NetData) *teleport.NetData { 14 | 15 | log.Println("---------enter room------------") 16 | 17 | uid := receive.From 18 | 19 | // 进行解码 20 | request := &server_proto.EnterRoomRequest{} 21 | err := proto.Unmarshal(receive.Body, request) 22 | if err != nil { 23 | log.Fatal("enter room request error: ", err) 24 | } 25 | 26 | //房间存在 27 | if table,ok := machine.GLOBAL_TABLE[int(request.RoomId)]; ok { 28 | log.Println("enter room ",table.TableId," success") 29 | 30 | //创建玩家,依据桌子房间(桌子)情况分配座位 31 | player := machine.CreatePlayer( uid, table ) 32 | //创建玩家的状态机 33 | player_machine := machine.NewPlayerMachine( &player, &machine.PlayerInitState{}, nil ) 34 | player.Machine = &player_machine 35 | //记录玩家信息到桌子 36 | table.PlayerDict[player.Seat] = &player 37 | //记录全局用户 38 | machine.GLOBAL_USER[player.Uid] = &player 39 | 40 | for key, value := range table.PlayerDict { 41 | log.Println("Key:", key, "Value:", value.Uid) 42 | } 43 | 44 | //是否全部准备 45 | //if table.IsAllReady() { 46 | // log.Println("all player are ready for game") 47 | // table.Machine.Trigger( &machine.TableReadyState{} ) 48 | //} 49 | 50 | } else { 51 | //return teleport.ReturnData(nil,"CreateRoomReturn",receive.From) 52 | } 53 | 54 | return nil 55 | } -------------------------------------------------------------------------------- /handlers/heartbeat.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | ) 7 | 8 | type Heartbeat struct{} 9 | 10 | func (*Heartbeat) Process(receive *teleport.NetData) *teleport.NetData { 11 | 12 | log.Println("-----------心跳----------",receive.From) 13 | 14 | return nil 15 | } 16 | -------------------------------------------------------------------------------- /handlers/identity.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | ) 7 | 8 | type Identity struct{} 9 | 10 | func (*Identity) Process(receive *teleport.NetData) *teleport.NetData { 11 | log.Println("=============identity===========") 12 | return nil 13 | } 14 | -------------------------------------------------------------------------------- /handlers/ready.go: -------------------------------------------------------------------------------- 1 | package handlers 2 | 3 | import ( 4 | "../teleport" 5 | "log" 6 | "../machine" 7 | ) 8 | 9 | type Ready struct {} 10 | 11 | func (*Ready) Process(receive *teleport.NetData) *teleport.NetData { 12 | 13 | log.Println("handler---------Ready------------") 14 | 15 | uid := receive.From 16 | 17 | //用户存在 18 | if player,ok := machine.GLOBAL_USER[uid]; ok { 19 | player.Machine.Trigger( &machine.PlayerReadyState{} ) 20 | } else { 21 | log.Printf("找不到用户") 22 | } 23 | 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /machine/action.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type Action struct { 4 | 5 | //用户触发的事件 6 | ActionId int 7 | 8 | //触发动作牌 9 | ActionCard int 10 | 11 | //手中参照牌 12 | ReferenceCard []int32 13 | 14 | //行为权重 15 | Weight int 16 | 17 | } 18 | -------------------------------------------------------------------------------- /machine/config.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type TableConfig struct { 4 | MaxChairs int 5 | MaxRounds int 6 | } 7 | func NewTableConfig() TableConfig{ 8 | return TableConfig{ 9 | 2, 10 | 2, 11 | } 12 | } -------------------------------------------------------------------------------- /machine/define.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | 4 | var GLOBAL_TABLE = make(map[int]*Table) 5 | 6 | var GLOBAL_USER = make(map[string]*Player) 7 | 8 | var PlayerEvent = map[string]string{ 9 | //玩家准备事件 10 | "PLAYER_EVENT_READY":"PLAYER_EVENT_READY", 11 | //其他玩家出牌事件 12 | "PLAYER_EVENT_OTHER_DISCARD":"PLAYER_EVENT_OTHER_DISCARD", 13 | //其他玩家杠牌 14 | "PLAYER_EVENT_OTHER_KONG":"PLAYER_EVENT_OTHER_KONG", 15 | //出牌 16 | "PLAYER_EVENT_DISCARD":"PLAYER_EVENT_DISCARD", 17 | //操作 18 | "PLAYER_EVENT_ACTION":"PLAYER_EVENT_ACTION", 19 | } 20 | 21 | var PlayerAction = map[string]int{ 22 | //碰牌 23 | "PLAYER_ACTION_PONG":1, 24 | //暗杠 25 | "PLAYER_ACTION_KONG_CONCEALED":2, 26 | //明杠 27 | "PLAYER_ACTION_KONG_EXPOSED":3, 28 | //过路杠 29 | "PLAYER_ACTION_KONG_PONG":4, 30 | //自摸 31 | "PLAYER_ACTION_WIN_DRAW":5, 32 | //点炮胡 33 | "PLAYER_ACTION_WIN_DISCARD":6, 34 | } 35 | 36 | var ClientPlayerAction = map[string]string { 37 | //抓牌 38 | "DRAW":"DRAW", 39 | //出牌 40 | "DISCARD":"DISCARD", 41 | //碰牌 42 | "PONG":"PONG", 43 | //明杠牌 44 | "KONG_EXPOSED":"KONG_EXPOSED", 45 | //暗杠牌 46 | "KONG_CONCEALED":"KONG_CONCEALED", 47 | //过路杠牌 48 | "KONG_PONG":"KONG_PONG", 49 | //自摸胡 50 | "WIN_DRAW":"WIN_DRAW", 51 | //点炮胡 52 | "WIN_DISCARD":"WIN_DISCARD", 53 | } 54 | 55 | var PlayerActionRule = map[int]PlayerRule{ 56 | //碰牌 57 | 1:&PlayerPongRule{}, 58 | //暗杠 59 | 2:&PlayerConcealedKongRule{}, 60 | //明杠 61 | 3:&PlayerExposedKongRule{}, 62 | //过路杠 63 | 4:&PlayerPongKongRule{}, 64 | //自摸 65 | 5:&PlayerDrawWinRule{}, 66 | //点炮胡 67 | 6:&PlayerDiscardWinRule{}, 68 | } 69 | 70 | var TableEvent = map[string]string{ 71 | //等待玩家准备 72 | "TABLE_EVENT_WAIT_READY":"TABLE_EVENT_WAIT_READY", 73 | //通知坐庄 74 | "TABLE_EVENT_PROMPT_DEAL":"TABLE_EVENT_PROMPT_DEAL", 75 | //步骤 76 | "TABLE_EVENT_STEP":"TABLE_EVENT_STEP", 77 | //结束 78 | "TABLE_EVENT_END":"TABLE_EVENT_END", 79 | } 80 | 81 | var Cards = map[int]string{ 82 | 1:"东风", 83 | 2:"西风", 84 | 3:"南风", 85 | 4:"北风", 86 | 5:"红中", 87 | 6:"发财", 88 | 7:"白板", 89 | 90 | 11:"1条", 91 | 12:"2条", 92 | 13:"3条", 93 | 14:"4条", 94 | 15:"5条", 95 | 16:"6条", 96 | 17:"7条", 97 | 18:"8条", 98 | 19:"9条", 99 | 100 | 21:"1筒", 101 | 22:"2筒", 102 | 23:"3筒", 103 | 24:"4筒", 104 | 25:"5筒", 105 | 26:"6筒", 106 | 27:"7筒", 107 | 28:"8筒", 108 | 29:"9筒", 109 | 110 | 31:"1万", 111 | 32:"2万", 112 | 33:"3万", 113 | 34:"4万", 114 | 35:"5万", 115 | 36:"6万", 116 | 37:"7万", 117 | 38:"8万", 118 | 39:"9万", 119 | } 120 | 121 | var WinTypes = map[string]int{ 122 | "WIN_DISCARD_ONE":1, 123 | "WIN_DISCARD_MORE":2, 124 | "WIN_DRAW":3, 125 | } 126 | 127 | var Scores = map[string]int{ 128 | "QI_XIAO_DUI":1, 129 | "QING_YI_SE":1, 130 | "PONG_PONG_HU":1, 131 | } -------------------------------------------------------------------------------- /machine/player.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "log" 5 | "reflect" 6 | "container/list" 7 | "../proto" 8 | "../global" 9 | ) 10 | 11 | type Player struct { 12 | 13 | //用户id 14 | Uid string 15 | 16 | //桌子 17 | Table *Table 18 | 19 | //座位号 20 | Seat int 21 | 22 | //前一位置 23 | PrevSeat int 24 | 25 | //下一位置 26 | NextSeat int 27 | 28 | //状态机 29 | Machine *PlayerMachine 30 | 31 | //是否在线 32 | OnlineState bool 33 | 34 | //====================================总局数值==================================== 35 | //总分 36 | ScoreTotal int 37 | 38 | //杠动作分 39 | ScoreKongTotal int 40 | 41 | //明杠次数 42 | KongExposedTotal int 43 | 44 | //暗杠次数 45 | KongConcealedTotal int 46 | 47 | //暗杠次数 48 | KongPongTotal int 49 | 50 | //放杠次数 51 | KongDiscardTotal int 52 | 53 | //自摸胡次数 54 | WinDrawCnt int 55 | 56 | //点炮胡次数 57 | WinDiscardCnt int 58 | 59 | //点炮次数 60 | PaoCnt int 61 | 62 | //====================================单轮数值==================================== 63 | //获得分数 64 | Score int 65 | 66 | //杠动作分 67 | KongScore int 68 | 69 | //手牌 70 | CardsInHand [14]int 71 | 72 | //碰杠牌 73 | CardsGroup []int 74 | 75 | //出过的牌 76 | CardsDiscard *list.List 77 | 78 | //可碰的牌 79 | CardsPong []int 80 | 81 | //可明杠的牌 82 | CardsKongExposed []int 83 | 84 | //可暗杠的牌 85 | CardsKongConcealed []int 86 | 87 | //可听的牌 88 | CardsReadyHand []int 89 | 90 | //可胡的牌 91 | CardsWin []int 92 | 93 | //明杠次数 94 | KongExposedCnt int 95 | 96 | //暗杠次数 97 | KongConcealedCnt int 98 | 99 | //过路杠次数 100 | KongPongCnt int 101 | 102 | //放杠次数 103 | KongDiscardCnt int 104 | 105 | //听牌提示 106 | CardsPrompt []int 107 | 108 | //漏碰的牌 109 | MissPongCards []int 110 | 111 | //漏胡的牌 112 | MissWinCards []int 113 | 114 | //过手胡分数 115 | MissWinCardScore int 116 | 117 | //抓的牌 118 | DrawCard int 119 | 120 | //过路杠的牌 121 | DrawKongExposedCard int 122 | 123 | //胡的牌 124 | WinCard int 125 | 126 | //胡牌类型:点炮 自摸 127 | WinType int 128 | 129 | //胡牌牌型 130 | WinFlag []string 131 | 132 | //是否有提示 133 | HasPrompt bool 134 | //提示自增id 135 | PromptId int 136 | 137 | //可执行动作集合 138 | ActionDict map[int]Action 139 | //选择的动作 140 | Action Action 141 | 142 | } 143 | 144 | func CreatePlayer( uid string, table *Table ) Player { 145 | var seat int 146 | //查找该用户是不是已经在成员列表 147 | flag := false 148 | for seat = 0; seat < table.Config.MaxChairs; seat++ { 149 | data, ok := table.PlayerDict[seat] 150 | if ok && data.Uid == uid { 151 | flag = true 152 | break 153 | } 154 | } 155 | //不在成员列表则取最低座位号 156 | if !flag { 157 | for seat = 0; seat < table.Config.MaxChairs; seat++ { 158 | _, ok := table.PlayerDict[seat] 159 | if !ok { 160 | break 161 | } 162 | } 163 | } 164 | 165 | return Player{ 166 | Uid: uid, 167 | Table: table, 168 | //需要修正 @debug 169 | Seat: seat, 170 | CardsDiscard:list.New(), 171 | } 172 | } 173 | 174 | func (self *Player) Ready() { 175 | self.Machine.Trigger( &PlayerReadyState{} ) 176 | } 177 | 178 | //去除玩家提示信息 179 | func ( self *Player ) DelPrompt() { 180 | self.HasPrompt = false 181 | self.PromptId = 0 182 | self.ActionDict = map[int]Action{} 183 | } 184 | 185 | //去除玩家动作信息 186 | func ( self *Player ) DelAction() { 187 | self.Action = Action{} 188 | } 189 | 190 | //玩家单轮数据重置 191 | func ( self *Player ) initRound() { 192 | //获得分数 193 | self.Score = 0 194 | //杠动作分 195 | self.KongScore = 0 196 | //手牌 197 | self.CardsInHand = [14]int{} 198 | //碰杠牌 199 | self.CardsGroup = []int{} 200 | //出过的牌 201 | self.CardsDiscard = list.New() 202 | //可碰的牌 203 | self.CardsPong = []int{} 204 | //可明杠的牌 205 | self.CardsKongExposed = []int{} 206 | //可暗杠的牌 207 | self.CardsKongConcealed = []int{} 208 | //可听的牌 209 | self.CardsReadyHand = []int{} 210 | //可胡的牌 211 | self.CardsWin = []int{} 212 | //明杠次数 213 | self.KongExposedCnt = 0 214 | //暗杠次数 215 | self.KongConcealedCnt = 0 216 | //过路杠次数 217 | self.KongPongCnt = 0 218 | //放杠次数 219 | self.KongDiscardCnt = 0 220 | //听牌提示 221 | self.CardsPrompt = []int{} 222 | //漏碰的牌 223 | self.MissPongCards = []int{} 224 | //漏胡的牌 225 | self.MissWinCards = []int{} 226 | //过手胡分数 227 | self.MissWinCardScore = 0 228 | //抓的牌 229 | self.DrawCard = 0 230 | //过路杠的牌 231 | self.DrawKongExposedCard = 0 232 | //胡的牌 233 | self.WinCard = 0 234 | //胡牌类型:点炮 自摸 235 | self.WinType = 0 236 | //胡牌牌型 237 | self.WinFlag = []string{} 238 | //是否有提示 239 | self.HasPrompt = false 240 | //提示自增id 241 | self.PromptId = 0 242 | //可执行动作集合 243 | self.ActionDict = map[int]Action{} 244 | //选择的动作 245 | self.Action = Action{} 246 | } 247 | 248 | //出牌 249 | func ( self *Player ) Discard( card int ) { 250 | log.Println(" ",self.Uid,"出牌",card) 251 | //如果用户是在没有处理操作提示的情况下出的牌 252 | if reflect.DeepEqual( self.Machine.CurrentState, &PlayerPromptState{} ) { 253 | log.Println("没有处理提示,直接出牌") 254 | //清除桌子提示 255 | self.Table.ClearPrompts() 256 | //立刻处理掉玩家提示状态下的状态切换 257 | self.Machine.NextState() 258 | } 259 | 260 | //出牌 261 | self.Table.DiscardSeat = self.Seat 262 | flag := false 263 | for k,v := range self.CardsInHand { 264 | if v == card { 265 | self.CardsInHand[k] = 0 266 | flag = true 267 | break 268 | } 269 | } 270 | //不存在的手牌 271 | if flag == false { 272 | log.Println(" ERROR:",self.Uid,"没有这张牌",card) 273 | return 274 | } 275 | log.Println(self.CardsInHand) 276 | self.CardsDiscard.PushFront(card) 277 | self.Table.ActiveCard = card 278 | 279 | //清除过手胡牌记录 280 | self.MissPongCards = []int{} 281 | self.MissWinCards = []int{} 282 | self.MissWinCardScore = 0 283 | 284 | //给所有人发出牌通知 285 | var request = &server_proto.ActionResponse{ 286 | self.Uid, 287 | int32(card), 288 | ClientPlayerAction["DISCARD"], 289 | []int32{}, 290 | } 291 | data := server_proto.MessageEncode( request ) 292 | for _,player := range self.Table.PlayerDict{ 293 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 294 | } 295 | 296 | //用户检测听牌状态 297 | self.CardsWin = ReadyHand( self.CardsInHand ) 298 | 299 | //其他玩家执行"他人出牌"判定 300 | for k,player := range self.Table.PlayerDict{ 301 | if player.Uid == self.Uid { 302 | continue 303 | } 304 | player.Machine.CurrentState.Execute( self.Table.PlayerDict[k], PlayerEvent["PLAYER_EVENT_OTHER_DISCARD"], nil ) 305 | } 306 | 307 | //杠上操作记录清空 308 | self.Table.KongStack = false 309 | self.Table.KongTriggerSeat = -1 310 | 311 | //他人执行判定后,可能出现操作,因此进入等待状态 312 | if len(self.Table.PlayerPrompts) > 0 { 313 | self.Machine.Trigger( &PlayerPauseState{} ) 314 | } else { 315 | self.Machine.NextState() 316 | } 317 | 318 | } 319 | 320 | //玩家操作 321 | func ( self *Player ) ActionSelect( select_id int ) { 322 | //选了过 323 | if select_id == 0 { 324 | log.Println(" ----",self.Uid," PASSED PROMPT----") 325 | } else { 326 | if action,ok := self.ActionDict[select_id]; ok { 327 | self.Action = action 328 | } else { 329 | log.Println("ERROR: ----NO SUCH PROMPT----") 330 | } 331 | } 332 | self.Table.PlayerActions = append( self.Table.PlayerActions, self.Seat ) 333 | 334 | //过掉低优先级操作 335 | //TODO 336 | //for _,seat := range self.Table.PlayerPrompts { 337 | // //不和自己比较 338 | // if seat == self.Seat { 339 | // continue 340 | // } 341 | // //已经操作完毕的用户跳过 342 | // if player,ok := self.Table.PlayerDict[seat]; ok { 343 | // if player.Action.Weight > 0 { 344 | // continue 345 | // } 346 | // //低优先级的未操作用户过滤掉 347 | // max_weight := 0 348 | // for _,action := range player.ActionDict { 349 | // if action.Weight > max_weight{ 350 | // max_weight = action.Weight 351 | // } 352 | // } 353 | // if max_weight < self.Action.Weight { 354 | // 355 | // } 356 | // } 357 | //} 358 | 359 | //相同优先级处理 360 | //TODO 361 | 362 | //桌子判定是否所有人已选择操作完毕 363 | self.Table.CheckAllActed() 364 | 365 | } -------------------------------------------------------------------------------- /machine/player_machine.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type PlayerMachine struct { 4 | 5 | Owner *Player 6 | 7 | CurrentState PlayerState 8 | 9 | LastState PlayerState 10 | 11 | } 12 | 13 | func NewPlayerMachine( player *Player, current PlayerState, last PlayerState ) PlayerMachine { 14 | return PlayerMachine{ 15 | Owner: player, 16 | CurrentState: current, 17 | LastState: last, 18 | } 19 | } 20 | 21 | func (self *PlayerMachine) Trigger( state PlayerState ) { 22 | if self.CurrentState != nil { 23 | self.CurrentState.Exit( self.Owner ) 24 | self.LastState = self.CurrentState 25 | } 26 | self.CurrentState = state 27 | self.CurrentState.Enter( self.Owner ) 28 | } 29 | 30 | func (self *PlayerMachine) BackToLastState() { 31 | self.Trigger(self.LastState) 32 | } 33 | 34 | func (self *PlayerMachine) Execute() { 35 | 36 | } 37 | 38 | func (self *PlayerMachine) NextState() { 39 | self.CurrentState.NextState( self.Owner ) 40 | } 41 | -------------------------------------------------------------------------------- /machine/player_manager.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "log" 5 | "reflect" 6 | ) 7 | 8 | var PlayerRulesGroup = map[string][]PlayerRule{ 9 | "PLAYER_RULE_READY": {}, 10 | "PLAYER_RULE_DRAW": {&PlayerConcealedKongRule{},&PlayerPongKongRule{},&PlayerDrawWinRule{}}, 11 | "PLAYER_RULE_DISCARD": {&PlayerPongRule{},&PlayerExposedKongRule{},&PlayerDiscardWinRule{}}, 12 | "PLAYER_RULE_DEAL": {}, 13 | "PLAYER_RULE_PONG":{&PlayerConcealedKongRule{},&PlayerPongKongRule{}}, 14 | "PLAYER_RULE_KONG": {}, //[QGWinRule()], 15 | //PLAYER_RULE_WIN: [], 16 | } 17 | 18 | func PlayerManagerCondition( player *Player, rule_group string ) { 19 | //依据检验的组对规则进行遍历 20 | if rules_array, ok := PlayerRulesGroup[rule_group]; ok { 21 | flag := false 22 | for _,rule := range rules_array { 23 | log.Println("检测",reflect.TypeOf(rule).String()) 24 | //满足规则则进行处理 25 | if rule.Condition( player ) { 26 | log.Println("满足",reflect.TypeOf(rule).String()) 27 | flag = true 28 | } 29 | } 30 | //规则检验有能够触发的,玩家进入提示状态 31 | if flag { 32 | player.Machine.Trigger( &PlayerPromptState{} ) 33 | } else { 34 | log.Println("player rule manager next1") 35 | player.Machine.CurrentState.NextState( player ) 36 | } 37 | } else { 38 | log.Println("Player Manager : rule_group Not Found") 39 | } 40 | 41 | return 42 | } -------------------------------------------------------------------------------- /machine/player_rule_state.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "../proto" 5 | "../global" 6 | ) 7 | 8 | //===========================玩家暗杠状态操作=========================== 9 | type PlayerConcealedKongRuleState struct {} 10 | func (this *PlayerConcealedKongRuleState) Enter( player *Player ) { 11 | log_PlayerState( player, "enter", "PlayerConcealedKongRuleState" ) 12 | 13 | active_card := player.Action.ActionCard 14 | card_num := 4 15 | for _,v := range player.CardsInHand { 16 | if v == active_card { 17 | card_num -- 18 | } 19 | } 20 | //有四张操作牌才能暗杠 21 | if card_num <= 0 { 22 | //手中移除4张牌 23 | num := 4 24 | for k,v := range player.CardsInHand { 25 | if v == active_card { 26 | player.CardsInHand[k] = 0 27 | num-- 28 | } 29 | if num == 0 { 30 | break 31 | } 32 | } 33 | //暗杠牌记录 34 | player.CardsKongConcealed = append( player.CardsKongConcealed, active_card ) 35 | //玩家暗杠操作记录+1 36 | player.KongConcealedCnt ++ 37 | //算暗杠分 38 | for _,p := range player.Table.PlayerDict{ 39 | //杠牌人+6,其他人-2 40 | if p.Uid == player.Uid { 41 | p.KongScore += 6 42 | } else { 43 | p.KongScore -= 2 44 | } 45 | } 46 | 47 | //通知其他玩家杠牌操作 48 | var request = &server_proto.ActionResponse{ 49 | player.Uid, 50 | int32(active_card), 51 | ClientPlayerAction["KONG_CONCEALED"], 52 | []int32{}, 53 | } 54 | data := server_proto.MessageEncode( request ) 55 | for _,player := range player.Table.PlayerDict{ 56 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 57 | } 58 | 59 | //去除出牌人记录 60 | player.Table.DiscardSeat = -1 61 | //当前活动人切换到自己 62 | player.Table.ActiveSeat = player.Seat 63 | 64 | //自己进入下一状态 65 | player.Machine.NextState() 66 | } 67 | 68 | } 69 | func (this *PlayerConcealedKongRuleState) Execute( player *Player, event string, request_body []byte ) { 70 | log_PlayerState( player, "execute", "PlayerConcealedKongRuleState" ) 71 | } 72 | func (this *PlayerConcealedKongRuleState) Exit( player *Player ) { 73 | log_PlayerState( player, "exit", "PlayerConcealedKongRuleState" ) 74 | } 75 | func (this *PlayerConcealedKongRuleState) NextState( player *Player ) { 76 | log_PlayerState( player, "next", "PlayerConcealedKongRuleState" ) 77 | //检测听牌 78 | //TODO 79 | 80 | player.Machine.Trigger( &PlayerDrawState{} ) 81 | } 82 | 83 | 84 | 85 | //===========================玩家过路杠状态操作=========================== 86 | type PlayerPongKongRuleState struct {} 87 | func (this *PlayerPongKongRuleState) Enter( player *Player ) { 88 | log_PlayerState( player, "enter", "PlayerPongKongRuleState" ) 89 | 90 | active_card := player.Action.ActionCard 91 | flag := 0 92 | for _,v := range player.CardsInHand { 93 | if v == active_card { 94 | flag++ 95 | break 96 | } 97 | } 98 | for _,v := range player.CardsPong { 99 | if v == active_card { 100 | flag++ 101 | break 102 | } 103 | } 104 | 105 | //手中有,已碰牌中也有才能过路杠 106 | if flag == 2 { 107 | for k,v := range player.CardsInHand { 108 | if v == active_card { 109 | player.CardsInHand[k] = 0 110 | break 111 | } 112 | } 113 | //明杠牌记录 114 | player.CardsKongExposed = append( player.CardsKongExposed, active_card ) 115 | //玩家过路杠操作记录+1 116 | player.KongPongCnt++ 117 | //算明杠分 118 | for _,p := range player.Table.PlayerDict{ 119 | //杠牌人+3,其他人-1 120 | if p.Uid == player.Uid { 121 | p.KongScore += 3 122 | } else { 123 | p.KongScore -= 1 124 | } 125 | } 126 | 127 | //通知其他玩家杠牌操作 128 | var request = &server_proto.ActionResponse{ 129 | player.Uid, 130 | int32(active_card), 131 | ClientPlayerAction["KONG_PONG"], 132 | []int32{}, 133 | } 134 | data := server_proto.MessageEncode( request ) 135 | for _,player := range player.Table.PlayerDict{ 136 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 137 | } 138 | 139 | //去除出牌人记录 140 | player.Table.DiscardSeat = -1 141 | 142 | //如果可以点炮胡,检验抢杠胡等状态 143 | player.Table.ClearPrompts() 144 | player.Table.ClearActions() 145 | for _,p := range player.Table.PlayerDict{ 146 | if p.Uid != player.Uid { 147 | p.Machine.CurrentState.Execute( p, PlayerEvent["PLAYER_EVENT_OTHER_KONG"], nil ) 148 | } 149 | } 150 | //发现有用户有可进行的操作,当前用户切换到暂停状态 151 | if len( player.Table.PlayerPrompts ) > 0 { 152 | player.Machine.Trigger( &PlayerPauseState{} ) 153 | return 154 | } 155 | 156 | //自己进入下一状态 157 | player.Machine.NextState() 158 | } 159 | 160 | } 161 | func (this *PlayerPongKongRuleState) Execute( player *Player, event string, request_body []byte ) { 162 | log_PlayerState( player, "execute", "PlayerPongKongRuleState" ) 163 | } 164 | func (this *PlayerPongKongRuleState) Exit( player *Player ) { 165 | log_PlayerState( player, "exit", "PlayerPongKongRuleState" ) 166 | } 167 | func (this *PlayerPongKongRuleState) NextState( player *Player ) { 168 | log_PlayerState( player, "next", "PlayerPongKongRuleState" ) 169 | //检测听牌 170 | //TODO 171 | 172 | player.Machine.Trigger( &PlayerDrawState{} ) 173 | } 174 | 175 | 176 | //===========================玩家明杠状态操作=========================== 177 | type PlayerExposedKongRuleState struct {} 178 | func (this *PlayerExposedKongRuleState) Enter( player *Player ) { 179 | log_PlayerState( player, "enter", "PlayerExposedKongRuleState" ) 180 | 181 | active_card := player.Action.ActionCard 182 | card_num := 3 183 | for _,v := range player.CardsInHand { 184 | if v == active_card { 185 | card_num -- 186 | } 187 | } 188 | //有三张操作牌才能明杠 189 | if card_num <= 0 { 190 | //手中移除3张牌 191 | num := 3 192 | for k,v := range player.CardsInHand { 193 | if v == active_card { 194 | player.CardsInHand[k] = 0 195 | num-- 196 | } 197 | if num == 0 { 198 | break 199 | } 200 | } 201 | //玩家明杠操作记录+1,并加三分 202 | player.CardsKongExposed = append( player.CardsKongExposed, active_card ) 203 | player.KongExposedCnt++ 204 | player.KongScore += 3 205 | //放杠人放杠记录+1,并扣三分 206 | trigger_player := player.Table.PlayerDict[ player.Table.ActiveSeat ] 207 | trigger_player.KongScore -= 3 208 | trigger_player.KongDiscardCnt++ 209 | //放杠人切换到等待状态 210 | trigger_player.Machine.Trigger(&PlayerWaitState{}) 211 | 212 | //通知其他玩家杠牌操作 213 | var request = &server_proto.ActionResponse{ 214 | player.Uid, 215 | int32(active_card), 216 | ClientPlayerAction["KONG_EXPOSED"], 217 | []int32{}, 218 | } 219 | data := server_proto.MessageEncode( request ) 220 | for _,player := range player.Table.PlayerDict{ 221 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 222 | } 223 | 224 | //去除出牌人记录 225 | player.Table.DiscardSeat = -1 226 | //当前活动人切换到自己 227 | player.Table.ActiveSeat = player.Seat 228 | 229 | //自己进入下一状态 230 | player.Machine.NextState() 231 | } 232 | 233 | } 234 | func (this *PlayerExposedKongRuleState) Execute( player *Player, event string, request_body []byte ) { 235 | log_PlayerState( player, "execute", "PlayerExposedKongRuleState" ) 236 | } 237 | func (this *PlayerExposedKongRuleState) Exit( player *Player ) { 238 | log_PlayerState( player, "exit", "PlayerExposedKongRuleState" ) 239 | } 240 | func (this *PlayerExposedKongRuleState) NextState( player *Player ) { 241 | log_PlayerState( player, "next", "PlayerExposedKongRuleState" ) 242 | //检测听牌 243 | //TODO 244 | 245 | player.Machine.Trigger( &PlayerDrawState{} ) 246 | } 247 | 248 | 249 | 250 | //===========================玩家碰状态操作=========================== 251 | type PlayerPongRuleState struct {} 252 | func (this *PlayerPongRuleState) Enter( player *Player ) { 253 | log_PlayerState( player, "enter", "PlayerPongRuleState" ) 254 | 255 | //手中必需有两张相同牌 256 | active_card := player.Action.ActionCard 257 | card_num := 2 258 | for _,v := range player.CardsInHand { 259 | if v == active_card { 260 | card_num -- 261 | } 262 | } 263 | if card_num <= 0 { 264 | //手牌去除两张 265 | card_num = 2 266 | for k,v := range player.CardsInHand { 267 | if v == active_card { 268 | card_num -- 269 | player.CardsInHand[k] = 0 270 | } 271 | if card_num == 0 { 272 | break 273 | } 274 | } 275 | //记录碰牌 276 | player.CardsPong = append( player.CardsPong, active_card ) 277 | 278 | //从出牌者手中去除出牌,并转换到等待状态 279 | discard_player := player.Table.PlayerDict[player.Table.ActiveSeat] 280 | e := discard_player.CardsDiscard.Front() 281 | discard_player.CardsDiscard.Remove(e) 282 | discard_player.Machine.Trigger( &PlayerWaitState{} ) 283 | 284 | //去除出牌人记录 285 | player.Table.DiscardSeat = -1 286 | 287 | //切换当前操作人 288 | player.Table.ActiveSeat = player.Seat 289 | 290 | //听牌提示 291 | //TODO 292 | 293 | //通知所有玩家碰牌操作 294 | var request = &server_proto.ActionResponse{ 295 | player.Uid, 296 | int32(active_card), 297 | ClientPlayerAction["PONG"], 298 | []int32{}, 299 | } 300 | data := server_proto.MessageEncode( request ) 301 | for _,player := range player.Table.PlayerDict{ 302 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 303 | } 304 | 305 | 306 | //该状态下规则检测 307 | log_PlayerState( player, "PLAYER_RULE_PONG", "checking..." ) 308 | PlayerManagerCondition( player, "PLAYER_RULE_PONG" ) 309 | } 310 | 311 | 312 | } 313 | func (this *PlayerPongRuleState) Execute( player *Player, event string, request_body []byte ) { 314 | log_PlayerState( player, "execute", "PlayerPongRuleState" ) 315 | } 316 | func (this *PlayerPongRuleState) Exit( player *Player ) { 317 | log_PlayerState( player, "exit", "PlayerPongRuleState" ) 318 | } 319 | func (this *PlayerPongRuleState) NextState( player *Player ) { 320 | log_PlayerState( player, "next", "PlayerPongRuleState" ) 321 | player.Machine.Trigger( &PlayerDiscardState{} ) 322 | } 323 | 324 | 325 | //===========================玩家自摸状态操作=========================== 326 | type PlayerDrawWinRuleState struct {} 327 | func (this *PlayerDrawWinRuleState) Enter( player *Player ) { 328 | log_PlayerState( player, "enter", "PlayerDrawWinRuleState" ) 329 | 330 | active_card := player.DrawCard 331 | 332 | //自摸记录+1 333 | player.WinDrawCnt += 1 334 | 335 | //记录赢家 336 | player.Table.WinnerList = append(player.Table.WinnerList,player.Seat) 337 | 338 | //杠上开花判断 339 | if player.Table.KongStack { 340 | player.WinFlag = append(player.WinFlag, "GSKH") 341 | } 342 | 343 | //检测手牌胡牌情况 344 | //TODO 345 | 346 | //算分 347 | //TODO 348 | 349 | //广播消息 350 | var request = &server_proto.ActionResponse{ 351 | player.Uid, 352 | int32(active_card), 353 | ClientPlayerAction["WIN_DRAW"], 354 | []int32{}, 355 | } 356 | data := server_proto.MessageEncode( request ) 357 | for _,player := range player.Table.PlayerDict{ 358 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 359 | } 360 | 361 | } 362 | func (this *PlayerDrawWinRuleState) Execute( player *Player, event string, request_body []byte ) { 363 | log_PlayerState( player, "execute", "PlayerDrawWinRuleState" ) 364 | } 365 | func (this *PlayerDrawWinRuleState) Exit( player *Player ) { 366 | log_PlayerState( player, "exit", "PlayerDrawWinRuleState" ) 367 | } 368 | func (this *PlayerDrawWinRuleState) NextState( player *Player ) { 369 | log_PlayerState( player, "next", "PlayerDrawWinRuleState" ) 370 | } 371 | 372 | 373 | //===========================玩家自摸状态操作=========================== 374 | type PlayerDiscardWinRuleState struct {} 375 | func (this *PlayerDiscardWinRuleState) Enter( player *Player ) { 376 | log_PlayerState( player, "enter", "PlayerDiscardWinRuleState" ) 377 | 378 | active_card := player.Table.ActiveCard 379 | 380 | //点炮胡记录+1 381 | player.WinDiscardCnt += 1 382 | 383 | //记录赢家 384 | player.Table.WinnerList = append(player.Table.WinnerList,player.Seat) 385 | 386 | //检测手牌胡牌情况 387 | //TODO 388 | 389 | //算分 390 | //TODO 391 | 392 | //广播消息 393 | var request = &server_proto.ActionResponse{ 394 | player.Uid, 395 | int32(active_card), 396 | ClientPlayerAction["WIN_DISCARD"], 397 | []int32{}, 398 | } 399 | data := server_proto.MessageEncode( request ) 400 | for _,player := range player.Table.PlayerDict{ 401 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 402 | } 403 | 404 | } 405 | func (this *PlayerDiscardWinRuleState) Execute( player *Player, event string, request_body []byte ) { 406 | log_PlayerState( player, "execute", "PlayerDiscardWinRuleState" ) 407 | } 408 | func (this *PlayerDiscardWinRuleState) Exit( player *Player ) { 409 | log_PlayerState( player, "exit", "PlayerDiscardWinRuleState" ) 410 | } 411 | func (this *PlayerDiscardWinRuleState) NextState( player *Player ) { 412 | log_PlayerState( player, "next", "PlayerDiscardWinRuleState" ) 413 | } -------------------------------------------------------------------------------- /machine/player_rules.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "log" 4 | 5 | type PlayerRule interface { 6 | //检验条件 7 | Condition( player *Player ) bool 8 | //进行处理 9 | Action( player *Player ) 10 | //进行处理 11 | AddPrompt( player *Player, action Action ) 12 | } 13 | 14 | func interface_player_rule_add_prompt( player *Player, action Action ){ 15 | player.HasPrompt = true 16 | player.PromptId += 1 17 | player.ActionDict[ player.PromptId ] = action 18 | 19 | } 20 | 21 | //==================================玩家暗杠==================================== 22 | type PlayerConcealedKongRule struct {} 23 | func (self *PlayerConcealedKongRule) Condition( player *Player ) bool { 24 | if player.Table.CardsRest.Len() <= 0 { 25 | return false 26 | } else { 27 | record := make(map[int]int) 28 | for _,v := range player.CardsInHand { 29 | if v == 0 { 30 | continue 31 | } else { 32 | record[v]++ 33 | } 34 | } 35 | log.Println(record) 36 | flag := false 37 | var action = Action{} 38 | for k,v := range record { 39 | if v >= 4 { 40 | action.ActionId = PlayerAction["PLAYER_ACTION_KONG_CONCEALED"] 41 | action.ActionCard = k 42 | action.Weight = PlayerAction["PLAYER_ACTION_KONG_CONCEALED"] 43 | self.AddPrompt( player, action ) 44 | flag = true 45 | } 46 | } 47 | return flag 48 | } 49 | } 50 | func (self *PlayerConcealedKongRule) Action( player *Player ) { 51 | player.Machine.Trigger( &PlayerConcealedKongRuleState{} ) 52 | } 53 | func (self *PlayerConcealedKongRule) AddPrompt( player *Player, action Action ) { 54 | interface_player_rule_add_prompt( player, action ) 55 | } 56 | 57 | 58 | //==================================玩家过路杠==================================== 59 | type PlayerPongKongRule struct {} 60 | func (self *PlayerPongKongRule) Condition( player *Player ) bool { 61 | if player.Table.CardsRest.Len() <= 0 || len(player.CardsPong) <= 0 { 62 | return false 63 | } else { 64 | flag := false 65 | var action = Action{} 66 | for _,v := range player.CardsPong { 67 | if v == 0 { 68 | continue 69 | } else { 70 | for _,card := range player.CardsInHand { 71 | if card == v { 72 | action.ActionId = PlayerAction["PLAYER_ACTION_KONG_PONG"] 73 | action.ActionCard = card 74 | action.Weight = PlayerAction["PLAYER_ACTION_KONG_PONG"] 75 | self.AddPrompt( player, action ) 76 | flag = true 77 | } 78 | } 79 | } 80 | } 81 | return flag 82 | } 83 | } 84 | func (self *PlayerPongKongRule) Action( player *Player ) { 85 | player.Machine.Trigger( &PlayerPongKongRuleState{} ) 86 | } 87 | func (self *PlayerPongKongRule) AddPrompt( player *Player, action Action ) { 88 | interface_player_rule_add_prompt( player, action ) 89 | } 90 | 91 | 92 | //==================================玩家明杠==================================== 93 | type PlayerExposedKongRule struct {} 94 | func (self *PlayerExposedKongRule) Condition( player *Player ) bool { 95 | if player.Table.CardsRest.Len() <= 0 { 96 | return false 97 | } else { 98 | //检测手中对应牌数量 99 | card_num := 0 100 | active_card := player.Table.ActiveCard 101 | for _,card := range player.CardsInHand { 102 | if card == active_card { 103 | card_num++ 104 | } 105 | } 106 | //大于三张才能明杠 107 | if card_num >= 3 { 108 | action := Action{ 109 | PlayerAction["PLAYER_ACTION_KONG_EXPOSED"], 110 | active_card, 111 | []int32{}, 112 | PlayerAction["PLAYER_ACTION_KONG_EXPOSED"], 113 | } 114 | self.AddPrompt( player, action ) 115 | return true 116 | } else { 117 | return false 118 | } 119 | } 120 | } 121 | func (self *PlayerExposedKongRule) Action( player *Player ) { 122 | player.Machine.Trigger( &PlayerExposedKongRuleState{} ) 123 | } 124 | func (self *PlayerExposedKongRule) AddPrompt( player *Player, action Action ) { 125 | interface_player_rule_add_prompt( player, action ) 126 | } 127 | 128 | 129 | //==================================玩家碰牌==================================== 130 | type PlayerPongRule struct {} 131 | func (self *PlayerPongRule) Condition( player *Player ) bool { 132 | active_card := player.Table.ActiveCard 133 | num := 0 134 | for _,v := range player.CardsInHand { 135 | if v == active_card { 136 | num++ 137 | } 138 | } 139 | if num >= 2 { 140 | action := Action{ 141 | PlayerAction["PLAYER_ACTION_PONG"], 142 | active_card, 143 | []int32{}, 144 | PlayerAction["PLAYER_ACTION_PONG"], 145 | } 146 | self.AddPrompt( player, action ) 147 | return true 148 | } else { 149 | return false 150 | } 151 | 152 | } 153 | func (self *PlayerPongRule) Action( player *Player ) { 154 | player.Machine.Trigger( &PlayerPongRuleState{} ) 155 | } 156 | func (self *PlayerPongRule) AddPrompt( player *Player, action Action ) { 157 | interface_player_rule_add_prompt( player, action ) 158 | } 159 | 160 | 161 | //==================================玩家自摸胡牌==================================== 162 | type PlayerDrawWinRule struct {} 163 | func (self *PlayerDrawWinRule) Condition( player *Player ) bool { 164 | //如果之前进行了听牌计算,可以直接使用听牌计算的结果判定 165 | draw_card := player.DrawCard 166 | flag := false 167 | for _,v := range player.CardsWin { 168 | if v == draw_card { 169 | flag = true 170 | } 171 | } 172 | //如果听牌结果因为某些原因不存在,则直接判定手牌 173 | if !flag { 174 | flag = WinCheck( player.CardsInHand ) 175 | } 176 | 177 | if flag { 178 | action := Action{ 179 | PlayerAction["PLAYER_ACTION_WIN_DRAW"], 180 | draw_card, 181 | []int32{}, 182 | PlayerAction["PLAYER_ACTION_WIN_DRAW"], 183 | } 184 | self.AddPrompt( player, action ) 185 | return true 186 | } else { 187 | return false 188 | } 189 | 190 | } 191 | func (self *PlayerDrawWinRule) Action( player *Player ) { 192 | player.Machine.Trigger( &PlayerDrawWinRuleState{} ) 193 | } 194 | func (self *PlayerDrawWinRule) AddPrompt( player *Player, action Action ) { 195 | interface_player_rule_add_prompt( player, action ) 196 | } 197 | 198 | 199 | //==================================玩家点炮胡牌==================================== 200 | type PlayerDiscardWinRule struct {} 201 | func (self *PlayerDiscardWinRule) Condition( player *Player ) bool { 202 | //当前出的牌 203 | active_card := player.Table.ActiveCard 204 | flag := false 205 | for _,v := range player.CardsWin { 206 | if v == active_card { 207 | flag = true 208 | } 209 | } 210 | if flag { 211 | action := Action{ 212 | PlayerAction["PLAYER_ACTION_WIN_DISCARD"], 213 | active_card, 214 | []int32{}, 215 | PlayerAction["PLAYER_ACTION_WIN_DISCARD"], 216 | } 217 | self.AddPrompt( player, action ) 218 | return true 219 | } else { 220 | return false 221 | } 222 | 223 | } 224 | func (self *PlayerDiscardWinRule) Action( player *Player ) { 225 | player.Machine.Trigger( &PlayerDiscardWinRuleState{} ) 226 | } 227 | func (self *PlayerDiscardWinRule) AddPrompt( player *Player, action Action ) { 228 | interface_player_rule_add_prompt( player, action ) 229 | } -------------------------------------------------------------------------------- /machine/player_state.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "log" 5 | "../proto" 6 | "../global" 7 | "reflect" 8 | ) 9 | 10 | type PlayerState interface { 11 | Enter( player *Player ) 12 | Execute( player *Player, event string, request_body []byte ) 13 | Exit( player *Player ) 14 | NextState( player *Player ) 15 | } 16 | 17 | func log_PlayerState( player *Player, act string, state string ) { 18 | log.Printf(" ----player %s %s %s ----",player.Uid,act,state) 19 | } 20 | 21 | func interface_player_execute( player *Player, event string ) bool { 22 | //当前状态下执行event事件 23 | define_event, ok := PlayerEvent[event] 24 | if ok && event == define_event { 25 | log.Printf(" player --%s-- ACTIVE -- %s ",player.Uid,event) 26 | } else { 27 | log.Println("PlayerInitState error call event "+event) 28 | } 29 | return ok 30 | } 31 | 32 | //===========================PlayerInitState=========================== 33 | type PlayerInitState struct {} 34 | func (this *PlayerInitState) Enter( player *Player ) { 35 | log_PlayerState( player, "enter", "init" ) 36 | } 37 | func (this *PlayerInitState) Execute( player *Player, event string, request_body []byte ) { 38 | log_PlayerState( player, "execute", "init" ) 39 | interface_player_execute( player, event ) 40 | } 41 | func (this *PlayerInitState) Exit( player *Player ) { 42 | log_PlayerState( player, "exit", "init" ) 43 | } 44 | func (this *PlayerInitState) NextState( player *Player ) { 45 | log_PlayerState( player, "next", "init" ) 46 | } 47 | 48 | 49 | //===========================PlayerReadyState=========================== 50 | type PlayerReadyState struct {} 51 | func (this *PlayerReadyState) Enter( player *Player ) { 52 | log_PlayerState( player, "enter", "ready" ) 53 | //广播 54 | 55 | 56 | //检测桌子状态 57 | player.Table.IsAllReady() 58 | 59 | } 60 | func (this *PlayerReadyState) Execute( player *Player, event string, request_body []byte ) { 61 | log_PlayerState( player, "execute", "ready" ) 62 | interface_player_execute( player, event ) 63 | } 64 | func (this *PlayerReadyState) Exit( player *Player ) { 65 | log_PlayerState( player, "exit", "ready" ) 66 | } 67 | func (this *PlayerReadyState) NextState( player *Player ) { 68 | log_PlayerState( player, "next", "ready" ) 69 | } 70 | 71 | 72 | //===========================PlayerDealState=========================== 73 | type PlayerDealState struct {} 74 | func (this *PlayerDealState) Enter( player *Player ) { 75 | log_PlayerState( player, "enter", "deal" ) 76 | //该状态下规则检测 77 | log_PlayerState( player, "PLAYER_RULE_DEAL", "checking..." ) 78 | PlayerManagerCondition( player, "PLAYER_RULE_DEAL" ) 79 | } 80 | func (this *PlayerDealState) Execute( player *Player, event string, request_body []byte ) { 81 | log_PlayerState( player, "execute", "deal" ) 82 | interface_player_execute( player, event ) 83 | } 84 | func (this *PlayerDealState) Exit( player *Player ) { 85 | log_PlayerState( player, "exit", "deal" ) 86 | } 87 | func (this *PlayerDealState) NextState( player *Player ) { 88 | log_PlayerState( player, "next", "deal" ) 89 | //玩家进入等待状态 90 | player.Machine.Trigger( &PlayerWaitState{} ) 91 | //桌子触发事件 92 | player.Table.Machine.CurrentState.Execute( player.Table, "TABLE_EVENT_PROMPT_DEAL", nil ) 93 | } 94 | 95 | 96 | //===========================PlayerWaitState=========================== 97 | type PlayerWaitState struct {} 98 | func (this *PlayerWaitState) Enter( player *Player ) { 99 | log_PlayerState( player, "enter", "wait" ) 100 | } 101 | func (this *PlayerWaitState) Execute( player *Player, event string, request_body []byte ) { 102 | log_PlayerState( player, "execute", "wait" ) 103 | if interface_player_execute( player, event ) { 104 | switch event { 105 | 106 | //他人出牌 107 | case PlayerEvent["PLAYER_EVENT_OTHER_DISCARD"] : 108 | PlayerManagerCondition( player, "PLAYER_RULE_DISCARD" ) 109 | //他人杠牌 110 | case PlayerEvent["PLAYER_EVENT_OTHER_KONG"] : 111 | PlayerManagerCondition( player, "PLAYER_RULE_KONG" ) 112 | 113 | default: 114 | log.Println("----- no such event ",event," ----- ") 115 | } 116 | } 117 | } 118 | func (this *PlayerWaitState) Exit( player *Player ) { 119 | log_PlayerState( player, "exit", "wait" ) 120 | } 121 | func (this *PlayerWaitState) NextState( player *Player ) { 122 | log_PlayerState( player, "next", "wait" ) 123 | } 124 | 125 | 126 | //===========================PlayerDrawState=========================== 127 | type PlayerDrawState struct {} 128 | func (this *PlayerDrawState) Enter( player *Player ) { 129 | log_PlayerState( player, "enter", "draw" ) 130 | 131 | //清除玩家上一个动作数据 132 | player.MissPongCards = []int{} 133 | player.MissWinCards = []int{} 134 | player.MissWinCardScore = 0 135 | 136 | //清除桌子提示和行为数据 137 | player.Table.ClearPrompts() 138 | player.Table.ClearActions() 139 | 140 | //当前用户听牌计算 141 | //TODO 142 | 143 | //用户抓牌 144 | e := player.Table.CardsRest.Front() 145 | player.Table.CardsRest.Remove(e) 146 | draw_card := e.Value.(int) 147 | player.DrawCard = draw_card 148 | for k,v := range player.CardsInHand { 149 | if v == 0 { 150 | player.CardsInHand[k] = draw_card 151 | break 152 | } 153 | } 154 | log.Println(player.Uid," : ",player.CardsInHand) 155 | 156 | //推送抓牌消息 157 | request := &server_proto.ActionResponse{ 158 | player.Uid, 159 | int32(draw_card), 160 | ClientPlayerAction["DRAW"], 161 | []int32{}, 162 | } 163 | data := server_proto.MessageEncode( request ) 164 | global.SERVER.Request(data, "ActionResponse", "action_response", player.Uid) 165 | 166 | //该状态下规则检测 167 | log_PlayerState( player, "PLAYER_RULE_DRAW", "checking..." ) 168 | PlayerManagerCondition( player, "PLAYER_RULE_DRAW" ) 169 | } 170 | func (this *PlayerDrawState) Execute( player *Player, event string, request_body []byte ) { 171 | log_PlayerState( player, "execute", "draw" ) 172 | interface_player_execute( player, event ) 173 | } 174 | func (this *PlayerDrawState) Exit( player *Player ) { 175 | log_PlayerState( player, "exit", "draw" ) 176 | } 177 | func (this *PlayerDrawState) NextState( player *Player ) { 178 | log_PlayerState( player, "next", "draw" ) 179 | player.Machine.Trigger( &PlayerDiscardState{} ) 180 | } 181 | 182 | 183 | //===========================PlayerDiscardState=========================== 184 | type PlayerDiscardState struct {} 185 | func (this *PlayerDiscardState) Enter( player *Player ) { 186 | log_PlayerState( player, "enter", "discard" ) 187 | 188 | //已经进入出牌步骤,清除之前操作记录 189 | player.Table.ClearPrompts() 190 | player.Table.ClearActions() 191 | } 192 | func (this *PlayerDiscardState) Execute( player *Player, event string, request_body []byte ) { 193 | log_PlayerState( player, "execute", "discard" ) 194 | if interface_player_execute( player, event ) { 195 | switch event { 196 | 197 | //出牌 198 | case PlayerEvent["PLAYER_EVENT_DISCARD"] : 199 | // 进行解码 200 | request := &server_proto.DiscardRequest{} 201 | server_proto.MessageDecode( request_body, request ) 202 | 203 | player.Discard( int(request.Card) ) 204 | 205 | default: 206 | log.Println("----- no such event ",event," ----- ") 207 | } 208 | } 209 | } 210 | func (this *PlayerDiscardState) Exit( player *Player ) { 211 | log_PlayerState( player, "exit", "discard" ) 212 | } 213 | func (this *PlayerDiscardState) NextState( player *Player ) { 214 | log_PlayerState( player, "next", "discard" ) 215 | player.Machine.Trigger( &PlayerWaitState{} ) 216 | player.Table.Machine.CurrentState.Execute( player.Table, TableEvent["TABLE_EVENT_STEP"], nil ) 217 | } 218 | 219 | //===========================PlayerPromptState=========================== 220 | type PlayerPromptState struct {} 221 | func (this *PlayerPromptState) Enter( player *Player ) { 222 | log_PlayerState( player, "enter", "prompt" ) 223 | //往桌子添加玩家提示记录 224 | player.Table.PlayerPrompts = append( player.Table.PlayerPrompts, player.Seat ) 225 | 226 | //给玩家发送提示 227 | request := &server_proto.ActionPrompt{} 228 | //var action_proto server_proto.Action 229 | for k,v := range player.ActionDict { 230 | action_proto := &server_proto.Action{ 231 | int32(k), 232 | int32(v.ActionId), 233 | int32(v.ActionCard), 234 | v.ReferenceCard, 235 | int32(v.Weight), 236 | } 237 | request.Action = append(request.Action, action_proto) 238 | } 239 | data := server_proto.MessageEncode( request ) 240 | global.SERVER.Request( data, "ActionPrompt","action_prompt_flag", player.Uid ) 241 | 242 | 243 | } 244 | func (this *PlayerPromptState) Execute( player *Player, event string, request_body []byte ) { 245 | log_PlayerState( player, "execute", "prompt" ) 246 | if interface_player_execute( player, event ) { 247 | switch event { 248 | 249 | //操作 250 | case PlayerEvent["PLAYER_EVENT_ACTION"] : 251 | // 进行解码 252 | request := &server_proto.ActionSelectRequest{} 253 | server_proto.MessageDecode( request_body, request ) 254 | 255 | player.ActionSelect( int(request.SelectId) ) 256 | 257 | //出牌 258 | case PlayerEvent["PLAYER_EVENT_DISCARD"] : 259 | // 进行解码 260 | request := &server_proto.DiscardRequest{} 261 | server_proto.MessageDecode( request_body, request ) 262 | 263 | player.Discard( int(request.Card) ) 264 | 265 | default: 266 | log.Println("----- no such event ",event," ----- ") 267 | } 268 | } 269 | } 270 | func (this *PlayerPromptState) Exit( player *Player ) { 271 | log_PlayerState( player, "exit", "prompt" ) 272 | } 273 | func (this *PlayerPromptState) NextState( player *Player ) { 274 | log_PlayerState( player, "next", "prompt" ) 275 | if reflect.DeepEqual( player.Machine.CurrentState, &PlayerWaitState{} ) { 276 | player.Machine.BackToLastState() 277 | } else { 278 | log.Println("player prompt execute next") 279 | player.Machine.LastState.NextState(player) 280 | } 281 | } 282 | 283 | 284 | //===========================PlayerPauseState=========================== 285 | type PlayerPauseState struct {} 286 | func (this *PlayerPauseState) Enter( player *Player ) { 287 | log_PlayerState( player, "enter", "pause" ) 288 | } 289 | func (this *PlayerPauseState) Execute( player *Player, event string, request_body []byte ) { 290 | log_PlayerState( player, "execute", "pause" ) 291 | if interface_player_execute( player, event ) { 292 | 293 | } 294 | } 295 | func (this *PlayerPauseState) Exit( player *Player ) { 296 | log_PlayerState( player, "exit", "pause" ) 297 | } 298 | func (this *PlayerPauseState) NextState( player *Player ) { 299 | log_PlayerState( player, "next", "pause" ) 300 | player.Machine.LastState.NextState( player ) 301 | } 302 | 303 | //===========================PlayerSettleState=========================== 304 | type PlayerSettleState struct {} 305 | func (this *PlayerSettleState) Enter( player *Player ) { 306 | log_PlayerState( player, "enter", "settle" ) 307 | } 308 | func (this *PlayerSettleState) Execute( player *Player, event string, request_body []byte ) { 309 | log_PlayerState( player, "execute", "settle" ) 310 | if interface_player_execute( player, event ) { 311 | switch event { 312 | 313 | //操作 314 | case PlayerEvent["PLAYER_EVENT_READY"] : 315 | player.Ready() 316 | 317 | default: 318 | log.Println("----- no such event ",event," ----- ") 319 | } 320 | } 321 | } 322 | func (this *PlayerSettleState) Exit( player *Player ) { 323 | log_PlayerState( player, "exit", "settle" ) 324 | } 325 | func (this *PlayerSettleState) NextState( player *Player ) { 326 | log_PlayerState( player, "next", "settle" ) 327 | } -------------------------------------------------------------------------------- /machine/table.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "time" 5 | "container/list" 6 | "math/rand" 7 | "log" 8 | "reflect" 9 | ) 10 | 11 | 12 | type Table struct { 13 | 14 | //桌子id 15 | TableId int 16 | 17 | //创建人 18 | OwnerId string 19 | 20 | //创建时间 21 | CreateTime time.Time 22 | 23 | //桌子配置 24 | Config TableConfig 25 | 26 | //玩家 27 | PlayerDict map[int] *Player 28 | 29 | //状态机 30 | Machine *TableMachine 31 | 32 | //庄家位置 33 | DealerSeat int 34 | 35 | //当前行动人位置 36 | ActiveSeat int 37 | 38 | //当前出牌 39 | ActiveCard int 40 | 41 | //当前出牌位置 42 | DiscardSeat int 43 | 44 | //当前执行事件 45 | Event string 46 | 47 | //剩余卡牌 48 | CardsRest *list.List 49 | 50 | //获得提示的玩家位置 51 | PlayerPrompts []int 52 | 53 | //做出行为选择的玩家位置 54 | PlayerActions []int 55 | 56 | //牌局记录 57 | Replay list.List 58 | 59 | //胡牌类型:流局,自摸胡,点炮胡,一炮多响 60 | WinType int 61 | 62 | //当前局数 63 | CurRound int 64 | 65 | //赢家 66 | WinnerList []int 67 | 68 | //输家 69 | LoserList []int 70 | 71 | //杠的栈记录 72 | KongStack bool 73 | 74 | //放杠人位置 75 | KongTriggerSeat int 76 | 77 | //斗庄 78 | DouCard int 79 | DouCard_num int 80 | IsDouZhuang bool 81 | 82 | } 83 | 84 | func CreateTable( oid string ) Table { 85 | rand.Seed(int64(time.Now().Nanosecond())) 86 | id := rand.Intn(89999) 87 | id += 10000 88 | return Table{ 89 | TableId:111111, 90 | OwnerId: oid, 91 | CreateTime: time.Now(), 92 | Config: NewTableConfig(), 93 | PlayerDict: make(map[int]*Player), 94 | //Replay: *list.New(), 95 | CurRound: 1, 96 | ActiveSeat:-1, 97 | } 98 | } 99 | 100 | func (self *Table) Enter() { 101 | //TODO 102 | 103 | } 104 | 105 | func (self *Table) DisMiss() { 106 | //房间进入结算状态,进行相关操作 107 | self.Machine.Trigger( &TableSettleForRoomState{} ) 108 | 109 | //删除房间内用户 110 | for _,v := range self.PlayerDict { 111 | delete( GLOBAL_USER, v.Uid ) 112 | } 113 | //删除桌子 114 | delete( GLOBAL_TABLE, self.TableId ) 115 | 116 | } 117 | 118 | func (self *Table) InitTable() Table { 119 | //TODO 120 | return *self 121 | } 122 | 123 | func (self *Table) InitRound() { 124 | //置为-1,在进入step状态后会取庄家位作为行动人 125 | self.ActiveSeat = -1 126 | //当前出牌 127 | self.ActiveCard = 0 128 | //当前出牌位置 129 | self.DiscardSeat = -1 130 | //获得提示的玩家位置 131 | self.PlayerPrompts = []int{} 132 | //做出行为选择的玩家位置 133 | self.PlayerActions = []int{} 134 | //牌局记录 135 | self.Replay = *list.New() 136 | //胡牌类型:流局,自摸胡,点炮胡,一炮多响 137 | self.WinType = 0 138 | //赢家 139 | self.WinnerList = []int{} 140 | //输家 141 | self.LoserList = []int{} 142 | //杠的栈记录 143 | self.KongStack = false 144 | //放杠人位置 145 | self.KongTriggerSeat = -1 146 | //玩家重置 147 | for _,p := range self.PlayerDict { 148 | p.initRound() 149 | } 150 | } 151 | 152 | func (self *Table) IsAllReady() bool { 153 | //人未到齐 154 | if len( self.PlayerDict ) != self.Config.MaxChairs { 155 | log.Println("人不齐") 156 | return false 157 | } 158 | //有用户未准备 159 | for _,player := range self.PlayerDict { 160 | if !reflect.DeepEqual( player.Machine.CurrentState, &PlayerReadyState{} ) { 161 | log.Println(player.Uid,"未准备") 162 | return false 163 | } 164 | } 165 | 166 | self.Machine.Trigger( &TableReadyState{} ) 167 | return true 168 | } 169 | 170 | func (self *Table) CheckAllActed() { 171 | //没有提示 172 | if len( self.PlayerPrompts ) == 0 { 173 | self.KongStack = false 174 | self.KongTriggerSeat = -1 175 | //玩家切换到下个状态 176 | log.Println("table all check next") 177 | self.PlayerDict[ self.ActiveSeat ].Machine.NextState() 178 | return 179 | } 180 | 181 | //还有玩家没有选择操作 182 | if len( self.PlayerActions ) < len( self.PlayerPrompts ) { 183 | log.Println("not all checked") 184 | return 185 | } 186 | log.Println("all checked") 187 | //清除桌子和玩家的提示记录 188 | self.ClearPrompts() 189 | 190 | //过滤选择出最高权重的操作 191 | max_weight := 0 192 | action_seats := []int{} 193 | for _,v := range self.PlayerActions { 194 | if player,ok := self.PlayerDict[v]; ok { 195 | if player.Action.Weight != 0 && player.Action.Weight > max_weight { 196 | max_weight = player.Action.Weight 197 | } 198 | } 199 | } 200 | for _,v := range self.PlayerActions { 201 | if player,ok := self.PlayerDict[v]; ok { 202 | if player.Action.Weight == max_weight { 203 | action_seats = append( action_seats, v ) 204 | } 205 | } 206 | } 207 | 208 | //选出来的玩家进行操作 209 | log.Println("+++操作中+++",max_weight," ",action_seats) 210 | for _,seat := range action_seats { 211 | if player,ok := self.PlayerDict[seat]; ok { 212 | action_id := player.Action.ActionId 213 | log.Println(action_id) 214 | if rule,ok := PlayerActionRule[action_id]; ok { 215 | log.Println(reflect.TypeOf(rule).String()) 216 | rule.Action( player ) 217 | } 218 | } 219 | } 220 | //清除桌子和玩家的操作记录 221 | self.ClearActions() 222 | 223 | //如果是杠牌相关操作,记录"杠"操作记录 224 | if max_weight == PlayerAction["PLAYER_ACTION_KONG_CONCEALED"] || 225 | max_weight == PlayerAction["PLAYER_ACTION_KONG_EXPOSED"] || 226 | max_weight == PlayerAction["PLAYER_ACTION_KONG_PONG"] { 227 | self.KongStack = true 228 | } else { 229 | self.KongStack = false 230 | self.KongTriggerSeat = -1 231 | } 232 | 233 | //如果是胡牌相关操作 234 | //自摸 235 | if max_weight == PlayerAction["PLAYER_ACTION_WIN_DRAW"] { 236 | if len(action_seats) != 1 { 237 | log.Fatalln( "Fatal Error : 自摸只可能有一人操作" ) 238 | } else { 239 | self.WinType = WinTypes["WIN_DRAW"] 240 | } 241 | self.Machine.Trigger( &TableEndState{} ) 242 | } 243 | //点炮胡 244 | if max_weight == PlayerAction["PLAYER_ACTION_WIN_DISCARD"] { 245 | if len(action_seats) > 1 { 246 | self.WinType = WinTypes["WIN_DISCARD_MORE"] 247 | } else if len( action_seats ) == 1 { 248 | self.WinType= WinTypes["WIN_DISCARD_ONE"] 249 | } 250 | self.Machine.Trigger( &TableEndState{} ) 251 | } 252 | 253 | 254 | return 255 | } 256 | 257 | //清除当前行为提示玩家记录 258 | func (self *Table) ClearPrompts() { 259 | self.PlayerPrompts = []int{} 260 | for _,player := range self.PlayerDict { 261 | player.DelPrompt() 262 | } 263 | } 264 | 265 | //清除当前行为选择玩家记录 266 | func (self *Table) ClearActions() { 267 | self.PlayerActions = []int{} 268 | for _,player := range self.PlayerDict { 269 | player.DelAction() 270 | } 271 | } 272 | 273 | //过滤行为数组,去除低优先级操作 274 | func (self *Table) FilterActions() { 275 | 276 | } 277 | 278 | func (self *Table) Shuffle() []int { 279 | vals := []int{ 280 | 1,1,1,1, 281 | 2,2,2,2, 282 | 3,3,3,3, 283 | 4,4,4,4, 284 | 5,5,5,5, 285 | 6,6,6,6, 286 | 7,7,7,7, 287 | 288 | 11,11,11,11, 289 | 12,12,12,12, 290 | 13,13,13,13, 291 | 14,14,14,14, 292 | 15,15,15,15, 293 | 16,16,16,16, 294 | 17,17,17,17, 295 | 18,18,18,18, 296 | 19,19,19,19, 297 | 298 | 21,21,21,21, 299 | 22,22,22,22, 300 | 23,23,23,23, 301 | 24,24,24,24, 302 | 25,25,25,25, 303 | 26,26,26,26, 304 | 27,27,27,27, 305 | 28,28,28,28, 306 | 29,29,29,29, 307 | 308 | 31,31,31,31, 309 | 32,32,32,32, 310 | 33,33,33,33, 311 | 34,34,34,34, 312 | 35,35,35,35, 313 | 36,36,36,36, 314 | 37,37,37,37, 315 | 38,38,38,38, 316 | 39,39,39,39, 317 | 318 | } 319 | r := rand.New(rand.NewSource(time.Now().Unix())) 320 | for _, i := range r.Perm(len(vals)) { 321 | val := vals[i] 322 | log.Println(val) 323 | } 324 | return vals 325 | } -------------------------------------------------------------------------------- /machine/table_machine.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | type TableMachine struct { 4 | 5 | Owner *Table 6 | 7 | CurrentState TableState 8 | 9 | LastState TableState 10 | 11 | } 12 | 13 | func NewTableMachine( table *Table, current TableState, last TableState ) TableMachine { 14 | return TableMachine{ 15 | Owner: table, 16 | CurrentState: current, 17 | LastState: last, 18 | } 19 | } 20 | 21 | func (self *TableMachine) Trigger( state TableState ) { 22 | if self.CurrentState != nil { 23 | self.CurrentState.Exit( self.Owner ) 24 | self.LastState = self.CurrentState 25 | } 26 | self.CurrentState = state 27 | self.CurrentState.Enter( self.Owner ) 28 | } 29 | 30 | func (self *TableMachine) BackToLastState() { 31 | 32 | } 33 | 34 | func (self *TableMachine) Execute() { 35 | 36 | } 37 | 38 | func (self *TableMachine) NextState() { 39 | 40 | } 41 | -------------------------------------------------------------------------------- /machine/table_manager.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | var TableRulesGroup = map[string][]TableRules{ 8 | "TABLE_RULE_READY":{}, 9 | "TABLE_RULE_DEAL":{}, 10 | "TABLE_RULE_STEP":{&TableLiuJuRule{}}, 11 | "TABLE_RULE_END":{}, 12 | "TABLE_RULE_SETTLE_FOR_ROUND":{&TableSettleForRoundRule{}}, 13 | } 14 | 15 | //===========================TableRulesManager=========================== 16 | //type TableRulesManager struct {} 17 | func TableManagerCondition( table *Table, rule_group string ) bool { 18 | //依据检验的组对规则进行遍历 19 | if rules_array, ok := TableRulesGroup[rule_group]; ok { 20 | for _,rule := range rules_array { 21 | //满足规则则进行处理 22 | if rule.Condition( table ) { 23 | rule.Action( table ) 24 | } 25 | } 26 | } else { 27 | log.Println("Manager : rule_group Not Found") 28 | } 29 | 30 | table.Machine.CurrentState.NextState( table ) 31 | return false 32 | } -------------------------------------------------------------------------------- /machine/table_rules.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "log" 4 | 5 | type TableRules interface { 6 | //检验条件 7 | Condition( table *Table ) bool 8 | //进行处理 9 | Action( table *Table ) 10 | } 11 | 12 | //==================================单轮结算==================================== 13 | type TableSettleForRoundRule struct {} 14 | func (self *TableSettleForRoundRule) Condition( table *Table ) bool { 15 | if table.CurRound > table.Config.MaxRounds { 16 | return true 17 | } else { 18 | return false 19 | } 20 | } 21 | func (self *TableSettleForRoundRule) Action( table *Table ) { 22 | 23 | } 24 | 25 | //==================================流局==================================== 26 | type TableLiuJuRule struct {} 27 | func (self *TableLiuJuRule) Condition( table *Table ) bool{ 28 | if table.CardsRest.Len() == 0 { 29 | log.Println("=====rule liuju true=====") 30 | return true 31 | } else { 32 | log.Println("=====rule liuju false=====") 33 | return false 34 | } 35 | } 36 | func (self *TableLiuJuRule) Action( table *Table ) { 37 | log.Println("=====rule liuju action=====") 38 | table.Machine.Trigger( &TableEndState{} ) 39 | } -------------------------------------------------------------------------------- /machine/table_state.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "log" 5 | "container/list" 6 | "reflect" 7 | ) 8 | 9 | type TableState interface { 10 | Enter( table *Table ) 11 | Execute( table *Table, event string, request_body []byte) 12 | Exit( table *Table ) 13 | NextState( table *Table ) 14 | } 15 | 16 | func interface_table_execute( table *Table, event string ) bool { 17 | //当前状态下执行event事件 18 | define_event, ok := TableEvent[event] 19 | if ok && event == define_event { 20 | log.Printf(" ====TABLE ======= ACTIVE == %s ",event) 21 | } else { 22 | log.Println("TableDealState error call event "+event) 23 | } 24 | return ok 25 | } 26 | 27 | //===========================TableReadyState=========================== 28 | type TableReadyState struct{} 29 | func (this *TableReadyState) Enter( table *Table ) { 30 | log.Println("===== TABLE ENTER READY STATE =====") 31 | //初始化单轮数据 32 | table.InitRound() 33 | //如果是第一局,先将玩家上下家定义好 34 | if table.CurRound == 1 { 35 | //定位上下家 36 | for seat, player := range table.PlayerDict { 37 | //下家 38 | next_seat := seat + 1; 39 | if next_seat >= table.Config.MaxChairs { 40 | next_seat -= table.Config.MaxChairs 41 | } 42 | player.NextSeat = next_seat 43 | 44 | //上家 45 | prev_seat := seat - 1 46 | if prev_seat < 0 { 47 | prev_seat += table.Config.MaxChairs 48 | } 49 | player.PrevSeat = prev_seat 50 | } 51 | } else { 52 | //不是第一局,局数后推,上轮数据清空 53 | table.CurRound++ 54 | } 55 | //该状态下规则检测 56 | log.Println(" ====TABLE_RULE_READY checking...") 57 | TableManagerCondition( table, "TABLE_RULE_READY" ) 58 | 59 | } 60 | func (this *TableReadyState) Execute( table *Table, event string, request_body []byte) { 61 | log.Println(" ====TABLE EXECUTE READY STATE") 62 | interface_table_execute( table, event ) 63 | } 64 | func (this *TableReadyState) Exit( table *Table ) { 65 | log.Println("===== TABLE EXIT READY STATE =====") 66 | } 67 | func (this *TableReadyState) NextState( table *Table ) { 68 | log.Println(" TABLE NEXT READY STATE") 69 | table.Machine.Trigger( &TableDealState{} ) 70 | } 71 | 72 | 73 | //===========================TableDealState=========================== 74 | type TableDealState struct{} 75 | func (this *TableDealState) Enter( table *Table ) { 76 | log.Println("===== TABLE ENTER DEAL STATE =====") 77 | 78 | //确定庄家 79 | table.DealerSeat = 0 80 | 81 | //开始发牌 82 | table.CardsRest = list.New() 83 | cards := table.Shuffle() 84 | for _,v := range cards { 85 | table.CardsRest.PushFront(v) 86 | } 87 | for position, player := range table.PlayerDict { 88 | for i:=1 ; i <= 13 ; i ++ { 89 | e := player.Table.CardsRest.Front() 90 | player.Table.CardsRest.Remove(e) 91 | draw_card := e.Value.(int) 92 | player.CardsInHand[i] = draw_card 93 | } 94 | player.CardsWin = ReadyHand( player.CardsInHand ) 95 | table.PlayerDict[position] = player 96 | } 97 | 98 | //所有用户触发状态 99 | for position := range table.PlayerDict { 100 | table.PlayerDict[position].Machine.Trigger( &PlayerDealState{} ) 101 | } 102 | } 103 | func (this *TableDealState) Execute( table *Table, event string, request_body []byte) { 104 | log.Println(" ====TABLE EXECUTE DEAL STATE") 105 | if interface_table_execute( table, event ) { 106 | //检测是否所有玩家都已进入等待状态 107 | for _,player := range table.PlayerDict { 108 | //如果有玩家不在等待状态 109 | if !reflect.DeepEqual( player.Machine.CurrentState, &PlayerWaitState{} ) { 110 | log.Printf(" TABLE EXECUTE DEAL : PLAYER %s NOT IN WAIT STATE",player.Uid) 111 | return 112 | } 113 | } 114 | log.Println(" ====TABLE EXECUTE DEAL : ALL IN WAIT STATE") 115 | table.Machine.Trigger( &TableStepState{} ) 116 | } 117 | } 118 | func (this *TableDealState) Exit( table *Table ) { 119 | log.Println("===== TABLE EXIT DEAL STATE =====") 120 | } 121 | func (this *TableDealState) NextState( table *Table ) { 122 | log.Println(" ====TABLE NEXT DEAL STATE") 123 | for _, player := range table.PlayerDict { 124 | log.Println(player.CardsInHand) 125 | } 126 | table.Machine.Trigger( &TableStepState{} ) 127 | } 128 | 129 | 130 | //===========================TableStepState=========================== 131 | type TableStepState struct{} 132 | func (this *TableStepState) Enter( table *Table ) { 133 | log.Println("===== TABLE ENTER STEP STATE =====") 134 | //该状态下规则检测 135 | log.Println(" ====TABLE_RULE_STEP checking...") 136 | TableManagerCondition( table, "TABLE_RULE_STEP" ) 137 | } 138 | func (this *TableStepState) Execute( table *Table, event string, request_body []byte) { 139 | log.Println(" ====TABLE EXECUTE STEP STATE") 140 | interface_table_execute( table, event ) 141 | } 142 | func (this *TableStepState) Exit( table *Table ) { 143 | log.Println("===== TABLE EXIT STEP STATE =====") 144 | } 145 | func (this *TableStepState) NextState( table *Table ) { 146 | log.Println(" ====TABLE NEXT STEP STATE") 147 | before_id := table.ActiveSeat 148 | //确定当前步骤执行人 149 | if table.ActiveSeat >= 0 { 150 | table.ActiveSeat = table.PlayerDict[ table.ActiveSeat ].NextSeat 151 | } else { 152 | table.ActiveSeat = table.DealerSeat 153 | } 154 | active_player := table.PlayerDict[ table.ActiveSeat ] 155 | log.Println("-------------------",before_id,"-----",table.ActiveSeat,"-----------") 156 | //桌子切换状态 157 | table.Machine.Trigger( &TableWaitState{} ) 158 | active_player.Machine.Trigger( &PlayerDrawState{} ) 159 | 160 | } 161 | 162 | 163 | //===========================TableWaitState=========================== 164 | type TableWaitState struct{} 165 | func (this *TableWaitState) Enter( table *Table ) { 166 | log.Println("===== TABLE ENTER WAIT STATE =====") 167 | } 168 | func (this *TableWaitState) Execute( table *Table, event string, request_body []byte) { 169 | log.Println(" ====TABLE EXECUTE WAIT STATE") 170 | if interface_table_execute( table, event ) { 171 | switch event { 172 | //执行步骤 173 | case TableEvent["TABLE_EVENT_STEP"] : 174 | table.Machine.Trigger( &TableStepState{} ) 175 | //结束 176 | case TableEvent["TABLE_EVENT_END"] : 177 | table.Machine.Trigger( &TableEndState{} ) 178 | 179 | default: 180 | log.Println("----- no such event ",event," ----- ") 181 | } 182 | } 183 | } 184 | func (this *TableWaitState) Exit( table *Table ) { 185 | log.Println("===== TABLE EXIT WAIT STATE =====") 186 | } 187 | func (this *TableWaitState) NextState( table *Table ) { 188 | log.Println(" ====TABLE NEXT WAIT STATE") 189 | } 190 | 191 | 192 | //===========================TableEndState=========================== 193 | type TableEndState struct{} 194 | func (this *TableEndState) Enter( table *Table ) { 195 | log.Println("===== TABLE ENTER END STATE =====") 196 | log.Println(" ====TABLE_RULE_END checking...") 197 | TableManagerCondition( table, "TABLE_RULE_END" ) 198 | } 199 | func (this *TableEndState) Execute( table *Table, event string, request_body []byte) { 200 | log.Println(" ====TABLE EXECUTE END STATE") 201 | interface_table_execute( table, event ) 202 | } 203 | func (this *TableEndState) Exit( table *Table ) { 204 | log.Println("===== TABLE EXIT END STATE =====") 205 | } 206 | func (this *TableEndState) NextState( table *Table ) { 207 | log.Println(" ====TABLE NEXT END STATE") 208 | table.Machine.Trigger( &TableSettleForRoundState{} ) 209 | } 210 | 211 | 212 | //===========================TableSettleForRoundState=========================== 213 | type TableSettleForRoundState struct{} 214 | func (this *TableSettleForRoundState) Enter( table *Table ) { 215 | log.Println("===== TABLE ENTER SETTLE FOR ROUND STATE =====") 216 | 217 | //玩家总局数据累计 218 | for _, player := range table.PlayerDict { 219 | player.ScoreTotal += player.Score 220 | player.ScoreKongTotal += player.KongScore 221 | player.KongExposedTotal += player.KongExposedCnt 222 | player.KongPongTotal += player.KongPongCnt 223 | player.KongConcealedTotal += player.KongConcealedCnt 224 | player.KongDiscardTotal += player.KongDiscardCnt 225 | } 226 | 227 | log.Println(" ====TABLE_RULE_SETTLE_FOR_ROUND checking...") 228 | TableManagerCondition( table, "TABLE_RULE_SETTLE_FOR_ROUND" ) 229 | } 230 | func (this *TableSettleForRoundState) Execute( table *Table, event string, request_body []byte) { 231 | log.Println(" ====TABLE EXECUTE SETTLE FOR ROUND STATE") 232 | interface_table_execute( table, event ) 233 | } 234 | func (this *TableSettleForRoundState) Exit( table *Table ) { 235 | log.Println("===== TABLE EXIT SETTLE FOR ROUND STATE =====") 236 | } 237 | func (this *TableSettleForRoundState) NextState( table *Table ) { 238 | log.Println(" ====TABLE NEXT SETTLE FOR ROUND STATE") 239 | table.Machine.Trigger( &TableRestartState{} ) 240 | } 241 | 242 | 243 | //===========================TableSettleForRoomState=========================== 244 | type TableSettleForRoomState struct{} 245 | func (this *TableSettleForRoomState) Enter( table *Table ) { 246 | log.Println("===== TABLE ENTER SETTLE FOR ROOM STATE =====") 247 | //广播 248 | //TODO 249 | } 250 | func (this *TableSettleForRoomState) Execute( table *Table, event string, request_body []byte) { 251 | log.Println(" ====TABLE EXECUTE SETTLE FOR ROOM STATE") 252 | interface_table_execute( table, event ) 253 | } 254 | func (this *TableSettleForRoomState) Exit( table *Table ) { 255 | log.Println("===== TABLE EXIT SETTLE FOR ROOM STATE =====") 256 | } 257 | func (this *TableSettleForRoomState) NextState( table *Table ) { 258 | log.Println(" ====TABLE NEXT SETTLE FOR ROOM STATE") 259 | } 260 | 261 | 262 | //===========================TableRestartState=========================== 263 | type TableRestartState struct{} 264 | func (this *TableRestartState) Enter( table *Table ) { 265 | log.Println("===== TABLE ENTER RESTART STATE =====") 266 | for _,p := range table.PlayerDict{ 267 | log.Println(p) 268 | } 269 | } 270 | func (this *TableRestartState) Execute( table *Table, event string, request_body []byte) { 271 | log.Println(" ====TABLE EXECUTE RESTART STATE") 272 | interface_table_execute( table, event ) 273 | } 274 | func (this *TableRestartState) Exit( table *Table ) { 275 | log.Println("===== TABLE EXIT RESTART STATE =====") 276 | } 277 | func (this *TableRestartState) NextState( table *Table ) { 278 | log.Println(" ====TABLE NEXT RESTART STATE") 279 | } -------------------------------------------------------------------------------- /machine/win_algorithm.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | func ReadyHand( hand_cards [14]int ) []int{ 4 | 5 | index := -1 6 | for k,v := range hand_cards{ 7 | if v == 0 { 8 | index = k 9 | } 10 | } 11 | 12 | result := []int{} 13 | 14 | if index == -1 { 15 | return result 16 | } 17 | 18 | for k,_ := range Cards{ 19 | hand_cards[index] = k 20 | if WinCheck(hand_cards) { 21 | result = append(result, k) 22 | } 23 | } 24 | 25 | return result 26 | } 27 | 28 | func WinCheck( hand_cards [14]int ) bool { 29 | 30 | cards := make(map[int]int) 31 | 32 | for _,v := range hand_cards{ 33 | cards[v]++ 34 | } 35 | 36 | if TryWin( false, cards ) { 37 | return true 38 | } else { 39 | return false 40 | } 41 | 42 | } 43 | 44 | func CountCard( self_cards map[int]int , card int ) int { 45 | if v, ok := self_cards[card]; ok { 46 | return v 47 | } else { 48 | return 0 49 | } 50 | } 51 | 52 | func TryWin( self_has_pair bool, self_cards map[int]int ) bool { 53 | 54 | rem_pair := self_has_pair 55 | rem_cards := make(map[int]int) 56 | for k,v := range self_cards{ 57 | rem_cards[k] = v 58 | } 59 | 60 | 61 | //取最小牌 62 | active_card := 99 63 | for k,v := range self_cards{ 64 | if k < active_card && v > 0 { 65 | active_card = k 66 | } 67 | } 68 | 69 | if active_card == 99 { 70 | if self_has_pair { 71 | return true 72 | } else { 73 | return false 74 | } 75 | } 76 | 77 | if TryPair( &self_has_pair, &self_cards, active_card ) { 78 | if TryWin( self_has_pair,self_cards ) { 79 | return true 80 | } 81 | } 82 | self_has_pair = rem_pair 83 | self_cards = rem_cards 84 | 85 | if TryTriplets( &self_cards, active_card ) { 86 | if TryWin( self_has_pair,self_cards ) { 87 | return true 88 | } 89 | } 90 | 91 | //if self.TrySequence( active_card ) { 92 | // if self.TryWin() { 93 | // return true 94 | // } 95 | //} 96 | 97 | return false 98 | } 99 | 100 | func TryPair( self_has_pair *bool, self_cards *(map[int]int), card int ) bool { 101 | if *self_has_pair { 102 | return false 103 | } 104 | if CountCard( *self_cards, card ) >= 2 { 105 | (*self_cards)[card] -= 2 106 | *self_has_pair = true 107 | return true 108 | } else { 109 | return false 110 | } 111 | 112 | } 113 | 114 | func TryTriplets( self_cards *(map[int]int), card int ) bool { 115 | if CountCard( *self_cards , card ) >= 3 { 116 | (*self_cards)[card] -= 3 117 | return true 118 | } else { 119 | return false 120 | } 121 | } 122 | 123 | //func TrySequence( card int ) bool { 124 | // 125 | //} 126 | -------------------------------------------------------------------------------- /proto/proto_func.go: -------------------------------------------------------------------------------- 1 | package server_proto 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "log" 6 | ) 7 | 8 | func MessageEncode( request proto.Message ) []byte { 9 | data, err := proto.Marshal( request ) 10 | if err != nil { 11 | log.Fatal("message encode error: ", err) 12 | } 13 | return data 14 | } 15 | 16 | func MessageDecode( data []byte, message proto.Message ) { 17 | // 进行解码 18 | err := proto.Unmarshal( data, message ) 19 | if err != nil { 20 | log.Fatal("create room request error: ", err) 21 | } 22 | } -------------------------------------------------------------------------------- /proto/server_proto.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: server_proto.proto 3 | 4 | /* 5 | Package server_proto is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | server_proto.proto 9 | 10 | It has these top-level messages: 11 | CreateRoomRequest 12 | CreateRoomResponse 13 | EnterRoomRequest 14 | EnterRoomResponse 15 | DrawCardResponse 16 | DiscardRequest 17 | ActionResponse 18 | Action 19 | ActionPrompt 20 | ActionSelectRequest 21 | */ 22 | package server_proto 23 | 24 | import proto "github.com/golang/protobuf/proto" 25 | import fmt "fmt" 26 | import math "math" 27 | 28 | // Reference imports to suppress errors if they are not otherwise used. 29 | var _ = proto.Marshal 30 | var _ = fmt.Errorf 31 | var _ = math.Inf 32 | 33 | // This is a compile-time assertion to ensure that this generated file 34 | // is compatible with the proto package it is being compiled against. 35 | // A compilation error at this line likely means your copy of the 36 | // proto package needs to be updated. 37 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 38 | 39 | // 客户端创建房间请求 40 | type CreateRoomRequest struct { 41 | Uuid string `protobuf:"bytes,1,opt,name=uuid" json:"uuid,omitempty"` 42 | Round int32 `protobuf:"varint,2,opt,name=round" json:"round,omitempty"` 43 | } 44 | 45 | func (m *CreateRoomRequest) Reset() { *m = CreateRoomRequest{} } 46 | func (m *CreateRoomRequest) String() string { return proto.CompactTextString(m) } 47 | func (*CreateRoomRequest) ProtoMessage() {} 48 | func (*CreateRoomRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 49 | 50 | func (m *CreateRoomRequest) GetUuid() string { 51 | if m != nil { 52 | return m.Uuid 53 | } 54 | return "" 55 | } 56 | 57 | func (m *CreateRoomRequest) GetRound() int32 { 58 | if m != nil { 59 | return m.Round 60 | } 61 | return 0 62 | } 63 | 64 | // 服务器创建房间返回 65 | type CreateRoomResponse struct { 66 | RoomId int32 `protobuf:"varint,1,opt,name=room_id,json=roomId" json:"room_id,omitempty"` 67 | } 68 | 69 | func (m *CreateRoomResponse) Reset() { *m = CreateRoomResponse{} } 70 | func (m *CreateRoomResponse) String() string { return proto.CompactTextString(m) } 71 | func (*CreateRoomResponse) ProtoMessage() {} 72 | func (*CreateRoomResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 73 | 74 | func (m *CreateRoomResponse) GetRoomId() int32 { 75 | if m != nil { 76 | return m.RoomId 77 | } 78 | return 0 79 | } 80 | 81 | // 客户端进入房间请求 82 | type EnterRoomRequest struct { 83 | RoomId int32 `protobuf:"varint,1,opt,name=room_id,json=roomId" json:"room_id,omitempty"` 84 | } 85 | 86 | func (m *EnterRoomRequest) Reset() { *m = EnterRoomRequest{} } 87 | func (m *EnterRoomRequest) String() string { return proto.CompactTextString(m) } 88 | func (*EnterRoomRequest) ProtoMessage() {} 89 | func (*EnterRoomRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 90 | 91 | func (m *EnterRoomRequest) GetRoomId() int32 { 92 | if m != nil { 93 | return m.RoomId 94 | } 95 | return 0 96 | } 97 | 98 | // 服务器进入房间返回 99 | type EnterRoomResponse struct { 100 | RoomId int32 `protobuf:"varint,1,opt,name=room_id,json=roomId" json:"room_id,omitempty"` 101 | } 102 | 103 | func (m *EnterRoomResponse) Reset() { *m = EnterRoomResponse{} } 104 | func (m *EnterRoomResponse) String() string { return proto.CompactTextString(m) } 105 | func (*EnterRoomResponse) ProtoMessage() {} 106 | func (*EnterRoomResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 107 | 108 | func (m *EnterRoomResponse) GetRoomId() int32 { 109 | if m != nil { 110 | return m.RoomId 111 | } 112 | return 0 113 | } 114 | 115 | // 服务端抽牌提示 116 | type DrawCardResponse struct { 117 | Card int32 `protobuf:"varint,1,opt,name=card" json:"card,omitempty"` 118 | } 119 | 120 | func (m *DrawCardResponse) Reset() { *m = DrawCardResponse{} } 121 | func (m *DrawCardResponse) String() string { return proto.CompactTextString(m) } 122 | func (*DrawCardResponse) ProtoMessage() {} 123 | func (*DrawCardResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 124 | 125 | func (m *DrawCardResponse) GetCard() int32 { 126 | if m != nil { 127 | return m.Card 128 | } 129 | return 0 130 | } 131 | 132 | // 客户端出牌请求 133 | type DiscardRequest struct { 134 | Card int32 `protobuf:"varint,1,opt,name=card" json:"card,omitempty"` 135 | } 136 | 137 | func (m *DiscardRequest) Reset() { *m = DiscardRequest{} } 138 | func (m *DiscardRequest) String() string { return proto.CompactTextString(m) } 139 | func (*DiscardRequest) ProtoMessage() {} 140 | func (*DiscardRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 141 | 142 | func (m *DiscardRequest) GetCard() int32 { 143 | if m != nil { 144 | return m.Card 145 | } 146 | return 0 147 | } 148 | 149 | // 服务器操作广播 150 | type ActionResponse struct { 151 | Uuid string `protobuf:"bytes,1,opt,name=uuid" json:"uuid,omitempty"` 152 | Card int32 `protobuf:"varint,2,opt,name=card" json:"card,omitempty"` 153 | ActionName string `protobuf:"bytes,3,opt,name=action_name,json=actionName" json:"action_name,omitempty"` 154 | RefCards []int32 `protobuf:"varint,4,rep,packed,name=ref_cards,json=refCards" json:"ref_cards,omitempty"` 155 | } 156 | 157 | func (m *ActionResponse) Reset() { *m = ActionResponse{} } 158 | func (m *ActionResponse) String() string { return proto.CompactTextString(m) } 159 | func (*ActionResponse) ProtoMessage() {} 160 | func (*ActionResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } 161 | 162 | func (m *ActionResponse) GetUuid() string { 163 | if m != nil { 164 | return m.Uuid 165 | } 166 | return "" 167 | } 168 | 169 | func (m *ActionResponse) GetCard() int32 { 170 | if m != nil { 171 | return m.Card 172 | } 173 | return 0 174 | } 175 | 176 | func (m *ActionResponse) GetActionName() string { 177 | if m != nil { 178 | return m.ActionName 179 | } 180 | return "" 181 | } 182 | 183 | func (m *ActionResponse) GetRefCards() []int32 { 184 | if m != nil { 185 | return m.RefCards 186 | } 187 | return nil 188 | } 189 | 190 | // 服务器操作推送单位 191 | type Action struct { 192 | SelectId int32 `protobuf:"varint,1,opt,name=select_id,json=selectId" json:"select_id,omitempty"` 193 | ActionId int32 `protobuf:"varint,2,opt,name=action_id,json=actionId" json:"action_id,omitempty"` 194 | ActionCard int32 `protobuf:"varint,3,opt,name=action_card,json=actionCard" json:"action_card,omitempty"` 195 | RefCards []int32 `protobuf:"varint,4,rep,packed,name=ref_cards,json=refCards" json:"ref_cards,omitempty"` 196 | Weight int32 `protobuf:"varint,5,opt,name=weight" json:"weight,omitempty"` 197 | } 198 | 199 | func (m *Action) Reset() { *m = Action{} } 200 | func (m *Action) String() string { return proto.CompactTextString(m) } 201 | func (*Action) ProtoMessage() {} 202 | func (*Action) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } 203 | 204 | func (m *Action) GetSelectId() int32 { 205 | if m != nil { 206 | return m.SelectId 207 | } 208 | return 0 209 | } 210 | 211 | func (m *Action) GetActionId() int32 { 212 | if m != nil { 213 | return m.ActionId 214 | } 215 | return 0 216 | } 217 | 218 | func (m *Action) GetActionCard() int32 { 219 | if m != nil { 220 | return m.ActionCard 221 | } 222 | return 0 223 | } 224 | 225 | func (m *Action) GetRefCards() []int32 { 226 | if m != nil { 227 | return m.RefCards 228 | } 229 | return nil 230 | } 231 | 232 | func (m *Action) GetWeight() int32 { 233 | if m != nil { 234 | return m.Weight 235 | } 236 | return 0 237 | } 238 | 239 | // 服务器操作推送 240 | type ActionPrompt struct { 241 | Action []*Action `protobuf:"bytes,1,rep,name=action" json:"action,omitempty"` 242 | } 243 | 244 | func (m *ActionPrompt) Reset() { *m = ActionPrompt{} } 245 | func (m *ActionPrompt) String() string { return proto.CompactTextString(m) } 246 | func (*ActionPrompt) ProtoMessage() {} 247 | func (*ActionPrompt) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } 248 | 249 | func (m *ActionPrompt) GetAction() []*Action { 250 | if m != nil { 251 | return m.Action 252 | } 253 | return nil 254 | } 255 | 256 | // 客户端操作选择 257 | type ActionSelectRequest struct { 258 | SelectId int32 `protobuf:"varint,1,opt,name=select_id,json=selectId" json:"select_id,omitempty"` 259 | } 260 | 261 | func (m *ActionSelectRequest) Reset() { *m = ActionSelectRequest{} } 262 | func (m *ActionSelectRequest) String() string { return proto.CompactTextString(m) } 263 | func (*ActionSelectRequest) ProtoMessage() {} 264 | func (*ActionSelectRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } 265 | 266 | func (m *ActionSelectRequest) GetSelectId() int32 { 267 | if m != nil { 268 | return m.SelectId 269 | } 270 | return 0 271 | } 272 | 273 | func init() { 274 | proto.RegisterType((*CreateRoomRequest)(nil), "CreateRoomRequest") 275 | proto.RegisterType((*CreateRoomResponse)(nil), "CreateRoomResponse") 276 | proto.RegisterType((*EnterRoomRequest)(nil), "EnterRoomRequest") 277 | proto.RegisterType((*EnterRoomResponse)(nil), "EnterRoomResponse") 278 | proto.RegisterType((*DrawCardResponse)(nil), "DrawCardResponse") 279 | proto.RegisterType((*DiscardRequest)(nil), "DiscardRequest") 280 | proto.RegisterType((*ActionResponse)(nil), "ActionResponse") 281 | proto.RegisterType((*Action)(nil), "Action") 282 | proto.RegisterType((*ActionPrompt)(nil), "ActionPrompt") 283 | proto.RegisterType((*ActionSelectRequest)(nil), "ActionSelectRequest") 284 | } 285 | 286 | func init() { proto.RegisterFile("server_proto.proto", fileDescriptor0) } 287 | 288 | var fileDescriptor0 = []byte{ 289 | // 344 bytes of a gzipped FileDescriptorProto 290 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xd1, 0x4a, 0xc3, 0x30, 291 | 0x14, 0x86, 0xe9, 0xba, 0x76, 0xdb, 0x99, 0x8c, 0x2d, 0x8a, 0x16, 0xbc, 0x58, 0x09, 0x22, 0x05, 292 | 0x75, 0xc2, 0xbc, 0xf6, 0x42, 0x36, 0x2f, 0x76, 0x23, 0x52, 0x1f, 0xa0, 0xc4, 0xf6, 0x4c, 0x0b, 293 | 0xb6, 0xa9, 0x49, 0xba, 0xbd, 0x8a, 0x8f, 0x2b, 0x4d, 0xda, 0xad, 0x30, 0x9c, 0x37, 0x25, 0xf9, 294 | 0xf3, 0x9d, 0xf3, 0xff, 0xe7, 0x50, 0x20, 0x12, 0xc5, 0x06, 0x45, 0x54, 0x08, 0xae, 0xf8, 0x4c, 295 | 0x7f, 0xe9, 0x23, 0x4c, 0x16, 0x02, 0x99, 0xc2, 0x90, 0xf3, 0x2c, 0xc4, 0xef, 0x12, 0xa5, 0x22, 296 | 0x04, 0xba, 0x65, 0x99, 0x26, 0x9e, 0xe5, 0x5b, 0xc1, 0x20, 0xd4, 0x67, 0x72, 0x06, 0x8e, 0xe0, 297 | 0x65, 0x9e, 0x78, 0x1d, 0xdf, 0x0a, 0x9c, 0xd0, 0x5c, 0xe8, 0x1d, 0x90, 0x76, 0xb9, 0x2c, 0x78, 298 | 0x2e, 0x91, 0x5c, 0x40, 0x4f, 0x70, 0x9e, 0x45, 0x75, 0x0b, 0x27, 0x74, 0xab, 0xeb, 0x2a, 0xa1, 299 | 0x37, 0x30, 0x7e, 0xce, 0x15, 0x8a, 0xb6, 0xd9, 0x9f, 0xf0, 0x2d, 0x4c, 0x5a, 0xf0, 0x7f, 0xad, 300 | 0xaf, 0x61, 0xbc, 0x14, 0x6c, 0xbb, 0x60, 0x22, 0xd9, 0xc1, 0x04, 0xba, 0x31, 0x13, 0x0d, 0xa9, 301 | 0xcf, 0xf4, 0x0a, 0x46, 0xcb, 0x54, 0xc6, 0x1a, 0xdb, 0x4d, 0x7b, 0x40, 0x6d, 0x60, 0xf4, 0x14, 302 | 0xab, 0x94, 0xe7, 0xed, 0x5e, 0x07, 0x3b, 0x69, 0x2a, 0x3b, 0xfb, 0x4a, 0x32, 0x85, 0x21, 0xd3, 303 | 0x95, 0x51, 0xce, 0x32, 0xf4, 0x6c, 0x8d, 0x83, 0x91, 0x5e, 0x58, 0x86, 0xe4, 0x12, 0x06, 0x02, 304 | 0xd7, 0x51, 0x05, 0x4b, 0xaf, 0xeb, 0xdb, 0x81, 0x13, 0xf6, 0x05, 0xae, 0xab, 0xe0, 0x92, 0xfe, 305 | 0x58, 0xe0, 0x1a, 0xe3, 0x8a, 0x93, 0xf8, 0x85, 0xb1, 0xda, 0xcf, 0xda, 0x37, 0xc2, 0x2a, 0xa9, 306 | 0x1e, 0x6b, 0x97, 0xb4, 0xb1, 0xef, 0x1b, 0x61, 0xd5, 0x8e, 0xa0, 0xd3, 0xd9, 0xfa, 0xb9, 0x8e, 307 | 0x50, 0xd9, 0x1c, 0x8d, 0x40, 0xce, 0xc1, 0xdd, 0x62, 0xfa, 0xf1, 0xa9, 0x3c, 0xc7, 0x2c, 0xd8, 308 | 0xdc, 0xe8, 0x3d, 0x9c, 0x98, 0x64, 0xaf, 0x82, 0x67, 0x85, 0x22, 0x53, 0x70, 0x4d, 0x4b, 0xcf, 309 | 0xf2, 0xed, 0x60, 0x38, 0xef, 0xcd, 0xea, 0x8d, 0xd5, 0x32, 0x9d, 0xc3, 0xa9, 0x51, 0xde, 0x74, 310 | 0xea, 0x66, 0xdd, 0xc7, 0xe6, 0x7a, 0x77, 0xf5, 0x5f, 0xf9, 0xf0, 0x1b, 0x00, 0x00, 0xff, 0xff, 311 | 0x9c, 0x9d, 0xa0, 0x0a, 0xab, 0x02, 0x00, 0x00, 312 | } 313 | -------------------------------------------------------------------------------- /proto/server_proto.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | //客户端创建房间请求 4 | message CreateRoomRequest { 5 | string uuid = 1; 6 | int32 round = 2; 7 | } 8 | //服务器创建房间返回 9 | message CreateRoomResponse { 10 | int32 room_id = 1; 11 | } 12 | //客户端进入房间请求 13 | message EnterRoomRequest { 14 | int32 room_id = 1; 15 | } 16 | //服务器进入房间返回 17 | message EnterRoomResponse { 18 | int32 room_id = 1; 19 | } 20 | //服务端抽牌提示 21 | message DrawCardResponse { 22 | int32 card = 1; 23 | } 24 | //客户端出牌请求 25 | message DiscardRequest { 26 | int32 card = 1; 27 | } 28 | //服务器操作广播 29 | message ActionResponse { 30 | string uuid = 1; 31 | int32 card = 2; 32 | string action_name = 3; 33 | repeated int32 ref_cards = 4; 34 | } 35 | //服务器操作推送单位 36 | message Action { 37 | int32 select_id = 1; 38 | int32 action_id = 2; 39 | int32 action_card = 3; 40 | repeated int32 ref_cards = 4; 41 | int32 weight = 5; 42 | } 43 | //服务器操作推送 44 | message ActionPrompt { 45 | repeated Action action = 1; 46 | } 47 | //客户端操作选择 48 | message ActionSelectRequest { 49 | int32 select_id = 1; 50 | } 51 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./teleport" 5 | "./teleport/debug" 6 | "./handlers" 7 | "./global" 8 | ) 9 | 10 | func main() { 11 | debug.Debug = true 12 | 13 | //注册请求处理函数 14 | serverHandlers := teleport.API{ 15 | "CreateRoom": new(handlers.CreateRoom), 16 | "EnterRoom": new(handlers.EnterRoom), 17 | "Discard": new(handlers.Discard), 18 | "ActionSelect": new(handlers.ActionSelect), 19 | "Ready": new(handlers.Ready), 20 | 21 | teleport.HEARTBEAT : new(handlers.Heartbeat), 22 | teleport.IDENTITY : new(handlers.Identity), 23 | } 24 | 25 | //启动服务器 26 | global.SERVER.SetUID("abc").SetAPI( serverHandlers ).Server(":20125") 27 | 28 | select {} 29 | } 30 | 31 | -------------------------------------------------------------------------------- /teleport/README.md: -------------------------------------------------------------------------------- 1 | # teleport [![GoDoc](https://godoc.org/github.com/tsuna/gohbase?status.png)](http://godoc.org/github.com/henrylee2cn/teleport) [![GitHub release](https://img.shields.io/github/release/henrylee2cn/teleport.svg)](https://github.com/henrylee2cn/teleport/releases) 2 | 3 | Teleport是一款适用于分布式系统的高并发API框架,它采用socket全双工通信,实现S/C对等工作,支持长、短两种连接模式,支持断开后自动连接与手动断开连接,内部数据传输格式为JSON。 4 | 5 | 6 | #### 框架模型 7 | ![image](https://github.com/henrylee2cn/teleport/raw/master/doc/Teleport API Model Diagram.png) 8 | 9 | 10 | #### 技术交流群 11 | Go-Web 编程 42730308 [![Go-Web 编程](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=Y0bksD) -------------------------------------------------------------------------------- /teleport/client.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | "github.com/FenrirLi/teleport/debug" 5 | "log" 6 | "net" 7 | "time" 8 | ) 9 | 10 | // 客户端专有成员 11 | type tpClient struct { 12 | // 客户端模式下,控制是否为短链接 13 | short bool 14 | // 强制终止客户端 15 | mustClose bool 16 | // 服务器UID 17 | serverUID string 18 | } 19 | 20 | // 启动客户端模式 21 | func (self *TP) Client(serverAddr string, port string, isShort ...bool) { 22 | if len(isShort) > 0 && isShort[0] { 23 | self.tpClient.short = true 24 | } else if self.timeout == 0 { 25 | // 默认心跳间隔时长 26 | self.timeout = DEFAULT_TIMEOUT_C 27 | } 28 | // 服务器UID默认为常量DEFAULT_SERVER_UID 29 | if self.tpClient.serverUID == "" { 30 | self.tpClient.serverUID = DEFAULT_SERVER_UID 31 | } 32 | self.mode = CLIENT 33 | 34 | // 设置端口 35 | if port != "" { 36 | self.port = port 37 | } else { 38 | self.port = DEFAULT_PORT 39 | } 40 | 41 | self.serverAddr = serverAddr 42 | 43 | self.tpClient.mustClose = false 44 | 45 | go self.apiHandle() 46 | go self.client() 47 | } 48 | 49 | // ***********************************************功能实现*************************************************** \\ 50 | 51 | // 以客户端模式启动 52 | func (self *TP) client() { 53 | if !self.short { 54 | log.Println(" * —— 正在连接服务器……") 55 | } 56 | 57 | RetryLabel: 58 | conn, err := net.Dial("tcp", self.serverAddr+self.port) 59 | if err != nil { 60 | if self.tpClient.mustClose { 61 | self.tpClient.mustClose = false 62 | return 63 | } 64 | time.Sleep(LOOP_TIMEOUT) 65 | goto RetryLabel 66 | } 67 | debug.Printf("Debug: 成功连接服务器: %v", conn.RemoteAddr().String()) 68 | 69 | // 开启该连接处理协程(读写两条协程) 70 | self.cGoConn(conn) 71 | 72 | // 与服务器意外断开后自动重拨 73 | if !self.short { 74 | for self.CountNodes() > 0 { 75 | time.Sleep(LOOP_TIMEOUT) 76 | } 77 | // 判断是否为意外断开 78 | if _, ok := self.connPool[self.tpClient.serverUID]; ok { 79 | goto RetryLabel 80 | } 81 | } 82 | } 83 | 84 | // 为连接开启读写两个协程 85 | func (self *TP) cGoConn(conn net.Conn) { 86 | remoteAddr, connect := NewConnect(conn, self.connBufferLen, self.connWChanCap) 87 | 88 | // 添加连接到节点池 89 | self.connPool[self.tpClient.serverUID] = connect 90 | 91 | if self.uid == "" { 92 | // 设置默认UID 93 | self.uid = conn.LocalAddr().String() 94 | } 95 | 96 | if !self.short { 97 | self.send(NewNetData(self.uid, self.tpClient.serverUID, IDENTITY, "", nil)) 98 | log.Printf(" * —— 成功连接到服务器:%v ——", remoteAddr) 99 | } else { 100 | connect.Short = true 101 | } 102 | 103 | // 标记连接已经正式生效可用 104 | self.connPool[self.tpClient.serverUID].Usable = true 105 | 106 | // 开启读写双工协程 107 | go self.cReader(self.tpClient.serverUID) 108 | go self.cWriter(self.tpClient.serverUID) 109 | } 110 | 111 | // 客户端读数据 112 | func (self *TP) cReader(nodeuid string) { 113 | // 退出时关闭连接,删除连接池中的连接 114 | defer func() { 115 | self.closeConn(nodeuid, true) 116 | }() 117 | 118 | var conn = self.getConn(nodeuid) 119 | 120 | for { 121 | if !self.read(conn) { 122 | break 123 | } 124 | } 125 | } 126 | 127 | // 客户端发送数据 128 | func (self *TP) cWriter(nodeuid string) { 129 | // 退出时关闭连接,删除连接池中的连接 130 | defer func() { 131 | self.closeConn(nodeuid, true) 132 | }() 133 | 134 | var conn = self.getConn(nodeuid) 135 | 136 | for conn != nil { 137 | if self.short { 138 | self.send(<-conn.WriteChan) 139 | continue 140 | } 141 | 142 | timing := time.After(self.timeout) 143 | data := new(NetData) 144 | select { 145 | case data = <-conn.WriteChan: 146 | case <-timing: 147 | // 保持心跳 148 | data = NewNetData(self.uid, nodeuid, HEARTBEAT, "", nil) 149 | } 150 | 151 | self.send(data) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /teleport/conn.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // 封装连接 8 | type Connect struct { 9 | // 标准包conn接口实例,继承该接口所有方法 10 | net.Conn 11 | // 标记连接是否有效 12 | Usable bool 13 | // 是否为短链接模式 14 | Short bool 15 | // 专用写入数据缓存通道 16 | WriteChan chan *NetData 17 | // 从连接循环接收数据 18 | Buffer []byte 19 | // 临时缓冲区,用来存储被截断的数据 20 | TmpBuffer []byte 21 | } 22 | 23 | // 创建Connect实例,默认为长连接(Short=false) 24 | func NewConnect(conn net.Conn, bufferLen int, wChanCap int) (k string, v *Connect) { 25 | k = conn.RemoteAddr().String() 26 | 27 | v = &Connect{ 28 | WriteChan: make(chan *NetData, wChanCap), 29 | Buffer: make([]byte, bufferLen), 30 | TmpBuffer: make([]byte, 0), 31 | Conn: conn, 32 | } 33 | return k, v 34 | } 35 | 36 | // 返回远程节点地址 37 | func (self *Connect) Addr() string { 38 | return self.Conn.RemoteAddr().String() 39 | } 40 | -------------------------------------------------------------------------------- /teleport/debug/debug.go: -------------------------------------------------------------------------------- 1 | // 打印调试 2 | package debug 3 | 4 | import ( 5 | "log" 6 | ) 7 | 8 | // 错误调试 9 | var Debug bool 10 | 11 | func Printf(format string, v ...interface{}) { 12 | if !Debug { 13 | return 14 | } 15 | log.Printf(format, v...) 16 | } 17 | 18 | func Println(v ...interface{}) { 19 | if !Debug { 20 | return 21 | } 22 | log.Println(v...) 23 | } 24 | 25 | func Fatal(v ...interface{}) { 26 | if !Debug { 27 | return 28 | } 29 | log.Fatal(v...) 30 | } 31 | -------------------------------------------------------------------------------- /teleport/netdata.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | // "net" 5 | ) 6 | 7 | const ( 8 | // 返回成功 9 | SUCCESS = 0 10 | // 返回失败 11 | FAILURE = -1 12 | // 返回非法请求 13 | LLLEGAL = -2 14 | ) 15 | 16 | // 定义数据传输结构 17 | type NetData struct { 18 | // 消息体 19 | Body []byte 20 | // 操作代号 21 | Operation string 22 | // 发信节点uid 23 | From string 24 | // 收信节点uid 25 | To string 26 | // 返回状态 27 | Status int 28 | // 标识符 29 | Flag string 30 | } 31 | 32 | func NewNetData(from, to, operation string, flag string, body []byte) *NetData { 33 | return &NetData{ 34 | From: from, 35 | To: to, 36 | Body: body, 37 | Operation: operation, 38 | Status: SUCCESS, 39 | Flag: flag, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /teleport/protocol.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | const ( 9 | // 支持数据最大长度为 2 << 61 10 | // DataLengthOfLenth = 8 11 | // 支持数据最大长度为 2 << 30 12 | DataLengthOfLenth = 4 13 | ) 14 | 15 | //通讯协议处理,主要处理封包和解包的过程 16 | type Protocol struct { 17 | // 包头 18 | header string 19 | // 包头长度 20 | headerLen int 21 | } 22 | 23 | func NewProtocol(packetHeader string) *Protocol { 24 | return &Protocol{ 25 | header: packetHeader, 26 | headerLen: len([]byte(packetHeader)), 27 | } 28 | } 29 | 30 | func (self *Protocol) ReSet(header string) { 31 | self.header = header 32 | self.headerLen = len([]byte(header)) 33 | } 34 | 35 | //封包 36 | func (self *Protocol) Packet(message []byte) []byte { 37 | return append(append([]byte(self.header), IntToBytes(len(message))...), message...) 38 | } 39 | 40 | //解包 41 | func (self *Protocol) Unpack(buffer []byte) (readerSlice [][]byte, bufferOver []byte) { 42 | length := len(buffer) 43 | 44 | var i int 45 | for i = 0; i < length; i = i + 1 { 46 | if length < i+self.headerLen+DataLengthOfLenth { 47 | break 48 | } 49 | if string(buffer[i:i+self.headerLen]) == self.header { 50 | messageLength := BytesToInt(buffer[i+self.headerLen : i+self.headerLen+DataLengthOfLenth]) 51 | if length < i+self.headerLen+DataLengthOfLenth+messageLength { 52 | break 53 | } 54 | data := buffer[i+self.headerLen+DataLengthOfLenth : i+self.headerLen+DataLengthOfLenth+messageLength] 55 | 56 | readerSlice = append(readerSlice, data) 57 | 58 | i += self.headerLen + DataLengthOfLenth + messageLength - 1 59 | } 60 | } 61 | 62 | if i == length { 63 | bufferOver = make([]byte, 0) 64 | return 65 | } 66 | bufferOver = buffer[i:] 67 | return 68 | } 69 | 70 | //整形转换成字节 71 | // func IntToBytes(n int) []byte { 72 | // x := int64(n) 73 | 74 | // bytesBuffer := bytes.NewBuffer([]byte{}) 75 | // binary.Write(bytesBuffer, binary.BigEndian, x) 76 | // return bytesBuffer.Bytes() 77 | // } 78 | 79 | // //字节转换成整形 80 | // func BytesToInt(b []byte) int { 81 | // bytesBuffer := bytes.NewBuffer(b) 82 | 83 | // var x int64 84 | // binary.Read(bytesBuffer, binary.BigEndian, &x) 85 | 86 | // return int(x) 87 | // } 88 | 89 | //整形转换成字节 90 | func IntToBytes(n int) []byte { 91 | x := int32(n) 92 | 93 | bytesBuffer := bytes.NewBuffer([]byte{}) 94 | binary.Write(bytesBuffer, binary.LittleEndian, x) 95 | return bytesBuffer.Bytes() 96 | } 97 | 98 | //字节转换成整形 99 | func BytesToInt(b []byte) int { 100 | bytesBuffer := bytes.NewBuffer(b) 101 | 102 | var x int32 103 | binary.Read(bytesBuffer, binary.LittleEndian, &x) 104 | 105 | return int(x) 106 | } 107 | -------------------------------------------------------------------------------- /teleport/return_func.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | // ***********************************************常用函数*************************************************** \\ 4 | // API中生成返回结果的方法 5 | // OpAndToAndFrom[0]参数为空时,系统将指定与对端相同的操作符 6 | // OpAndToAndFrom[1]参数为空时,系统将指定与对端为接收者 7 | // OpAndToAndFrom[2]参数为空时,系统将指定自身为发送者 8 | func ReturnData(body []byte, OpAndToAndFrom ...string) *NetData { 9 | data := &NetData{ 10 | Status: SUCCESS, 11 | Body: body, 12 | } 13 | if len(OpAndToAndFrom) > 0 { 14 | data.Operation = OpAndToAndFrom[0] 15 | } 16 | if len(OpAndToAndFrom) > 1 { 17 | data.To = OpAndToAndFrom[1] 18 | } 19 | if len(OpAndToAndFrom) > 2 { 20 | data.From = OpAndToAndFrom[2] 21 | } 22 | return data 23 | } 24 | 25 | // 返回错误,receive建议为直接接收到的*NetData 26 | func ReturnError(receive *NetData, status int, msg []byte, nodeuid ...string) *NetData { 27 | receive.Status = status 28 | receive.Body = msg 29 | receive.From = "" 30 | if len(nodeuid) > 0 { 31 | receive.To = nodeuid[0] 32 | } else { 33 | receive.To = "" 34 | } 35 | return receive 36 | } 37 | -------------------------------------------------------------------------------- /teleport/server.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/FenrirLi/teleport/debug" 6 | "log" 7 | "net" 8 | "time" 9 | ) 10 | 11 | // 服务器专有成员 12 | type tpServer struct { 13 | // 服务器模式下,缓存监听对象 14 | listener net.Listener 15 | } 16 | 17 | // 启动服务器模式,端口默认为常量DEFAULT_PORT 18 | func (self *TP) Server(port ...string) { 19 | self.mode = SERVER 20 | // 设置端口 21 | if len(port) > 0 { 22 | self.port = port[0] 23 | } else { 24 | self.port = DEFAULT_PORT 25 | } 26 | // 服务器UID默认为常量DEFAULT_SERVER_UID 27 | if self.uid == "" { 28 | self.uid = DEFAULT_SERVER_UID 29 | } 30 | if self.timeout == 0 { 31 | // 默认心跳间隔时长 32 | self.timeout = DEFAULT_TIMEOUT_S 33 | } 34 | go self.apiHandle() 35 | go self.server() 36 | } 37 | 38 | // ***********************************************功能实现*************************************************** \\ 39 | 40 | // 以服务器模式启动 41 | func (self *TP) server() { 42 | var err error 43 | retry: 44 | self.listener, err = net.Listen("tcp", self.port) 45 | if err != nil { 46 | debug.Printf("Debug: 监听端口出错: %v", err) 47 | time.Sleep(LOOP_TIMEOUT) 48 | goto retry 49 | } 50 | 51 | log.Printf(" * —— 已开启服务器监听 (port %v) ——", self.port) 52 | 53 | for self.listener != nil { 54 | // 等待下一个连接,如果没有连接,listener.Accept会阻塞 55 | conn, err := self.listener.Accept() 56 | if err != nil { 57 | return 58 | } 59 | 60 | log.Printf(" * 客户端 %v 已连接,但尚未验证身份!", self.port) 61 | debug.Printf("Debug: 客户端 %v 已连接,但尚未验证身份!", conn.RemoteAddr().String()) 62 | 63 | // 开启该连接处理协程(读写两条协程) 64 | self.sGoConn(conn) 65 | } 66 | } 67 | 68 | // 为每个连接开启读写两个协程 69 | func (self *TP) sGoConn(conn net.Conn) { 70 | remoteAddr, connect := NewConnect(conn, self.connBufferLen, self.connWChanCap) 71 | // 初始化节点 72 | nodeuid, ok := self.sInitConn(connect, remoteAddr) 73 | if !ok { 74 | conn.Close() 75 | return 76 | } 77 | 78 | // 开启读写双工协程 79 | go self.sReader(nodeuid) 80 | go self.sWriter(nodeuid) 81 | } 82 | 83 | // 连接初始化,绑定节点与连接,默认key为节点ip 84 | func (self *TP) sInitConn(conn *Connect, remoteAddr string) (nodeuid string, usable bool) { 85 | read_len, err := conn.Read(conn.Buffer) 86 | if err != nil || read_len == 0 { 87 | return 88 | } 89 | // 解包 90 | conn.TmpBuffer = append(conn.TmpBuffer, conn.Buffer[:read_len]...) 91 | dataSlice := make([][]byte, 10) 92 | dataSlice, conn.TmpBuffer = self.Unpack(conn.TmpBuffer) 93 | 94 | for i, data := range dataSlice { 95 | debug.Println("Debug: 收到数据-第1批-解码前: ", string(data)) 96 | 97 | d := new(NetData) 98 | json.Unmarshal(data, d) 99 | // 修复缺失请求方地址的请求 100 | if d.From == "" { 101 | d.From = remoteAddr // 或可为:strings.Split(remoteAddr, ":")[0] 102 | } 103 | 104 | if i == 0 { 105 | debug.Printf("Debug: 收到数据-第1条-NetData: %+v", d) 106 | 107 | // 检查连接权限 108 | if !self.checkRights(d, remoteAddr) { 109 | return 110 | } 111 | 112 | nodeuid = d.From 113 | 114 | // 添加连接到节点池 115 | self.connPool[nodeuid] = conn 116 | 117 | // 判断是否为短链接 118 | if d.Operation != IDENTITY { 119 | conn.Short = true 120 | } else { 121 | log.Printf(" * —— 客户端 %v (%v) 连接成功 ——", nodeuid, remoteAddr) 122 | } 123 | 124 | // 标记连接已经正式生效可用 125 | conn.Usable = true 126 | } 127 | // 添加到读取缓存 128 | self.apiReadChan <- d 129 | } 130 | return nodeuid, true 131 | } 132 | 133 | // 服务器读数据 134 | func (self *TP) sReader(nodeuid string) { 135 | // 退出时关闭连接,删除连接池中的连接 136 | defer func() { 137 | self.closeConn(nodeuid, false) 138 | }() 139 | 140 | var conn = self.getConn(nodeuid) 141 | 142 | for conn != nil { 143 | // 设置连接超时 144 | if !conn.Short { 145 | conn.SetReadDeadline(time.Now().Add(self.timeout)) 146 | } 147 | // 等待读取数据 148 | if !self.read(conn) { 149 | return 150 | } 151 | } 152 | } 153 | 154 | // 服务器发送数据 155 | func (self *TP) sWriter(nodeuid string) { 156 | defer func() { 157 | self.closeConn(nodeuid, false) 158 | }() 159 | 160 | var conn = self.getConn(nodeuid) 161 | 162 | for conn != nil { 163 | data := <-conn.WriteChan 164 | self.send(data) 165 | if conn.Short { 166 | return 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /teleport/teleport.go: -------------------------------------------------------------------------------- 1 | // Teleport是一款适用于分布式系统的高并发API框架,它采用socket全双工通信,实现S/C对等工作,支持长、短两种连接模式,支持断开后自动连接与手动断开连接,内部数据传输格式为JSON。 2 | package teleport 3 | 4 | import ( 5 | "encoding/json" 6 | "github.com/FenrirLi/teleport/debug" 7 | "log" 8 | "time" 9 | ) 10 | 11 | // mode 12 | const ( 13 | SERVER = iota + 1 14 | CLIENT 15 | ) 16 | 17 | // API中定义操作时必须保留的字段 18 | const ( 19 | // 身份登记 20 | IDENTITY = "+identity+" 21 | // 心跳操作符 22 | HEARTBEAT = "+heartbeat+" 23 | // 默认包头 24 | DEFAULT_PACK_HEADER = "Fenrir" 25 | // SERVER默认UID 26 | DEFAULT_SERVER_UID = "server" 27 | // 默认端口 28 | DEFAULT_PORT = ":8080" 29 | // 服务器默认心跳间隔时长 30 | DEFAULT_TIMEOUT_S = 60e9 31 | // 客户端默认心跳间隔时长 32 | DEFAULT_TIMEOUT_C = 30e9 33 | // 等待连接的轮询时长 34 | LOOP_TIMEOUT = 1e9 35 | ) 36 | 37 | type Teleport interface { 38 | // *以服务器模式运行,端口默认为常量DEFAULT_PORT 39 | Server(port ...string) 40 | // *以客户端模式运行,port为空时默认等于常量DEFAULT_PORT 41 | Client(serverAddr string, port string, isShort ...bool) 42 | // *主动推送信息,不写nodeuid默认随机发送给一个节点 43 | Request(body []byte, operation string, flag string, nodeuid ...string) 44 | // 指定自定义的应用程序API 45 | SetAPI(api API) Teleport 46 | // 断开连接,参数为空则断开所有连接,服务器模式下还将停止监听 47 | Close(nodeuid ...string) 48 | 49 | // 设置唯一标识符,mine为本节点UID(默认ip:port) 50 | // server为服务器UID(默认为常量DEFAULT_SERVER_UID,此参数仅客户端模式下有用) 51 | // 可不调用该方法,此时UID均为默认 52 | SetUID(mine string, server ...string) Teleport 53 | // 设置包头字符串,默认为henrylee2cn 54 | SetPackHeader(string) Teleport 55 | // 设置指定API处理的数据的接收缓存通道长度 56 | SetApiRChan(int) Teleport 57 | // 设置每个连接对象的发送缓存通道长度 58 | SetConnWChan(int) Teleport 59 | // 设置每个连接对象的接收缓冲区大小 60 | SetConnBuffer(int) Teleport 61 | // 设置连接超时(心跳频率) 62 | SetTimeout(time.Duration) Teleport 63 | 64 | //保留api 65 | ReserveAPI() Teleport 66 | 67 | // 返回运行模式 68 | GetMode() int 69 | // 返回当前有效连接节点数 70 | CountNodes() int 71 | } 72 | 73 | type TP struct { 74 | // 本节点唯一标识符 75 | uid string 76 | // 运行模式 1 SERVER 2 CLIENT (用于判断自身模式) 77 | mode int 78 | // 服务器端口号,格式如":9988" 79 | port string 80 | // 服务器地址(不含端口号),格式如"127.0.0.1" 81 | serverAddr string 82 | // 长连接池,刚一连接时key为host:port形式,随后通过身份验证替换为UID 83 | connPool map[string]*Connect 84 | // 连接时长,心跳时长的依据,默认为常量DEFAULT_TIMEOUT 85 | timeout time.Duration 86 | // 粘包处理 87 | *Protocol 88 | // 全局接收缓存通道 89 | apiReadChan chan *NetData 90 | // 每个连接对象的发送缓存通道长度 91 | connWChanCap int 92 | // 每个连接对象的接收缓冲区大小 93 | connBufferLen int 94 | // 应用程序API 95 | api API 96 | // 服务器模式专有成员 97 | *tpServer 98 | // 客户端模式专有成员 99 | *tpClient 100 | } 101 | 102 | // 每个API方法需判断status状态,并做相应处理 103 | type API map[string]Handle 104 | 105 | // 请求处理接口 106 | type Handle interface { 107 | Process(*NetData) *NetData 108 | } 109 | 110 | // 创建接口实例 111 | func New() Teleport { 112 | return &TP{ 113 | connPool: make(map[string]*Connect), 114 | api: API{}, 115 | Protocol: NewProtocol(DEFAULT_PACK_HEADER), 116 | apiReadChan: make(chan *NetData, 4096), 117 | connWChanCap: 2048, 118 | connBufferLen: 1024, 119 | tpServer: new(tpServer), 120 | tpClient: new(tpClient), 121 | } 122 | } 123 | 124 | // ***********************************************实现接口*************************************************** \\ 125 | 126 | // 设置唯一标识符,mine为本节点UID(默认ip:port) 127 | // server为服务器UID(默认为常量DEFAULT_SERVER_UID,此参数仅客户端模式下有用) 128 | // 可不调用该方法,此时UID均为默认 129 | func (self *TP) SetUID(mine string, server ...string) Teleport { 130 | if len(server) > 0 { 131 | self.tpClient.serverUID = server[0] 132 | } 133 | self.uid = mine 134 | return self 135 | } 136 | 137 | // 指定应用程序API 138 | func (self *TP) SetAPI(api API) Teleport { 139 | self.api = api 140 | return self 141 | } 142 | 143 | // *主动推送信息,直到有连接出现开始发送,不写nodeuid默认随机发送给一个节点 144 | func (self *TP) Request(body []byte, operation string, flag string, nodeuid ...string) { 145 | var conn *Connect 146 | var uid string 147 | if len(nodeuid) == 0 { 148 | for { 149 | if self.CountNodes() > 0 { 150 | break 151 | } 152 | time.Sleep(LOOP_TIMEOUT) 153 | } 154 | // 一个随机节点的信息 155 | for uid, conn = range self.connPool { 156 | if conn.Usable { 157 | nodeuid = append(nodeuid, uid) 158 | break 159 | } 160 | } 161 | } 162 | // 等待并取得连接实例 163 | conn = self.getConn(nodeuid[0]) 164 | for conn == nil || !conn.Usable { 165 | conn = self.getConn(nodeuid[0]) 166 | time.Sleep(LOOP_TIMEOUT) 167 | } 168 | conn.WriteChan <- NewNetData(self.uid, nodeuid[0], operation, flag, body) 169 | // log.Println("添加一条请求:", conn.RemoteAddr().String(), operation, body) 170 | } 171 | 172 | // 断开连接,参数为空则断开所有连接,服务器模式下停止监听 173 | func (self *TP) Close(nodeuid ...string) { 174 | if self.mode == CLIENT { 175 | self.tpClient.mustClose = true 176 | 177 | } else if self.mode == SERVER && self.tpServer.listener != nil { 178 | self.tpServer.listener.Close() 179 | log.Printf(" * —— 服务器已终止监听 %v! ——", self.port) 180 | } 181 | 182 | if len(nodeuid) == 0 { 183 | // 断开全部连接 184 | for uid, conn := range self.connPool { 185 | delete(self.connPool, uid) 186 | conn.Close() 187 | self.closeMsg(uid, conn.Addr(), conn.Short) 188 | } 189 | return 190 | } 191 | 192 | for _, uid := range nodeuid { 193 | conn := self.connPool[uid] 194 | delete(self.connPool, uid) 195 | conn.Close() 196 | self.closeMsg(uid, conn.Addr(), conn.Short) 197 | } 198 | } 199 | 200 | // 设置包头字符串,默认为henrylee2cn 201 | func (self *TP) SetPackHeader(header string) Teleport { 202 | self.Protocol.ReSet(header) 203 | return self 204 | } 205 | 206 | // 设置全局接收缓存通道长度 207 | func (self *TP) SetApiRChan(length int) Teleport { 208 | self.apiReadChan = make(chan *NetData, length) 209 | return self 210 | } 211 | 212 | // 设置每个连接对象的发送缓存通道长度 213 | func (self *TP) SetConnWChan(length int) Teleport { 214 | self.connWChanCap = length 215 | return self 216 | } 217 | 218 | // 每个连接对象的接收缓冲区大小 219 | func (self *TP) SetConnBuffer(length int) Teleport { 220 | self.connBufferLen = length 221 | return self 222 | } 223 | 224 | // 设置连接超长(心跳频率) 225 | func (self *TP) SetTimeout(long time.Duration) Teleport { 226 | self.timeout = long 227 | return self 228 | } 229 | 230 | // 返回运行模式 231 | func (self *TP) GetMode() int { 232 | return self.mode 233 | } 234 | 235 | // 返回当前有效连接节点数 236 | func (self *TP) CountNodes() int { 237 | count := 0 238 | for _, conn := range self.connPool { 239 | if conn != nil && conn.Usable { 240 | count++ 241 | } 242 | } 243 | return count 244 | } 245 | 246 | // ***********************************************公用方法*************************************************** \\ 247 | 248 | func (self *TP) read(conn *Connect) bool { 249 | read_len, err := conn.Read(conn.Buffer) 250 | if err != nil { 251 | return false 252 | } 253 | if read_len == 0 { 254 | return false // connection already closed by client 255 | } 256 | conn.TmpBuffer = append(conn.TmpBuffer, conn.Buffer[:read_len]...) 257 | self.save(conn) 258 | return true 259 | } 260 | 261 | // 根据uid获取连接对象 262 | func (self *TP) getConn(nodeuid string) *Connect { 263 | return self.connPool[nodeuid] 264 | } 265 | 266 | // 根据uid获取连接对象地址 267 | func (self *TP) getConnAddr(nodeuid string) string { 268 | conn := self.getConn(nodeuid) 269 | if conn == nil { 270 | // log.Printf("已与节点 %v 失去连接,无法完成发送请求!", nodeuid) 271 | return "" 272 | } 273 | return conn.Addr() 274 | } 275 | 276 | // 关闭连接,退出协程 277 | func (self *TP) closeConn(nodeuid string, reconnect bool) { 278 | conn, ok := self.connPool[nodeuid] 279 | if !ok { 280 | return 281 | } 282 | 283 | // 是否允许自动重连 284 | if reconnect { 285 | self.connPool[nodeuid] = nil 286 | } else { 287 | delete(self.connPool, nodeuid) 288 | } 289 | 290 | if conn == nil { 291 | return 292 | } 293 | conn.Close() 294 | self.closeMsg(nodeuid, conn.Addr(), conn.Short) 295 | } 296 | 297 | // 关闭连接时log信息 298 | func (self *TP) closeMsg(uid, addr string, short bool) { 299 | if short { 300 | return 301 | } 302 | switch self.mode { 303 | case SERVER: 304 | log.Printf(" * —— 与客户端 %v (%v) 断开连接!——", uid, addr) 305 | case CLIENT: 306 | log.Printf(" * —— 与服务器 %v 断开连接!——", addr) 307 | } 308 | } 309 | 310 | // 通信数据编码与发送 311 | func (self *TP) send(data *NetData) { 312 | if data.From == "" { 313 | data.From = self.uid 314 | } 315 | 316 | d, err := json.Marshal(*data) 317 | if err != nil { 318 | debug.Println("Debug: 发送数据-编码出错", err) 319 | return 320 | } 321 | conn := self.getConn(data.To) 322 | if conn == nil { 323 | debug.Printf("Debug: 发送数据-连接已断开: %+v", data) 324 | return 325 | } 326 | // 封包 327 | end := self.Packet(d) 328 | // 发送 329 | conn.Write(end) 330 | debug.Printf("Debug: 发送数据-成功: %+v", data) 331 | } 332 | 333 | // 解码收到的数据并存入缓存 334 | func (self *TP) save(conn *Connect) { 335 | debug.Printf("Debug: 收到数据-Byte: %v", conn.TmpBuffer) 336 | // 解包 337 | dataSlice := make([][]byte, 10) 338 | dataSlice, conn.TmpBuffer = self.Unpack(conn.TmpBuffer) 339 | 340 | for _, data := range dataSlice { 341 | debug.Printf("Debug: 收到数据-解码前: %v", string(data)) 342 | 343 | d := new(NetData) 344 | err := json.Unmarshal(data, d) 345 | if err == nil { 346 | // 修复缺失请求方地址的请求 347 | if d.From == "" { 348 | d.From = conn.Addr() 349 | } 350 | // 添加到读取缓存 351 | self.apiReadChan <- d 352 | debug.Printf("Debug: 收到数据-NetData: %+v", d) 353 | } else { 354 | debug.Printf("Debug: 收到数据-解码错误: %v", err) 355 | } 356 | } 357 | } 358 | 359 | // 使用API并发处理请求 360 | func (self *TP) apiHandle() { 361 | for { 362 | req := <-self.apiReadChan 363 | go func(req *NetData) { 364 | var conn *Connect 365 | 366 | operation, from, to, flag := req.Operation, req.To, req.From, req.Flag 367 | handle, ok := self.api[operation] 368 | 369 | // 非法请求返回错误 370 | if !ok { 371 | // log.Printf("%+v", req) 372 | if self.mode == SERVER { 373 | //self.autoErrorHandle(req, LLLEGAL, "服务器 ("+self.getConn(to).LocalAddr().String()+") 不存在API接口: "+req.Operation+"!", to) 374 | log.Printf("客户端 %v (%v) 正在请求不存在的API接口: %v!", to, self.getConnAddr(to), req.Operation) 375 | } else { 376 | //self.autoErrorHandle(req, LLLEGAL, "客户端 "+from+" ("+self.getConn(to).LocalAddr().String()+") 不存在API接口: "+req.Operation+"!", to) 377 | log.Printf("服务器 (%v) 正在请求不存在的API接口: %v!", self.getConnAddr(to), req.Operation) 378 | } 379 | return 380 | } 381 | 382 | resp := handle.Process(req) 383 | if resp == nil { 384 | if conn = self.getConn(to); conn != nil && self.getConn(to).Short { 385 | self.closeConn(to, false) 386 | } 387 | return //continue 388 | } 389 | 390 | if resp.To == "" { 391 | resp.To = to 392 | } 393 | 394 | // 若指定节点连接不存在,则向原请求端返回错误 395 | if conn = self.getConn(resp.To); conn == nil { 396 | self.autoErrorHandle(req, FAILURE, nil, to) 397 | return 398 | } 399 | 400 | // 默认指定与req相同的操作符 401 | if resp.Operation == "" { 402 | resp.Operation = operation 403 | } 404 | 405 | if resp.From == "" { 406 | resp.From = from 407 | } 408 | 409 | if resp.Flag == "" { 410 | resp.Flag = flag 411 | } 412 | 413 | conn.WriteChan <- resp 414 | 415 | }(req) 416 | } 417 | } 418 | 419 | func (self *TP) autoErrorHandle(data *NetData, status int, msg []byte, reqFrom string) bool { 420 | oldConn := self.getConn(reqFrom) 421 | if oldConn == nil { 422 | return false 423 | } 424 | respErr := ReturnError(data, status, msg) 425 | respErr.From = self.uid 426 | respErr.To = reqFrom 427 | oldConn.WriteChan <- respErr 428 | return true 429 | } 430 | 431 | // 连接权限验证 432 | func (self *TP) checkRights(data *NetData, addr string) bool { 433 | if data.To != self.uid { 434 | log.Printf("陌生连接(%v)提供的服务器标识符错误,请求被拒绝!", addr) 435 | return false 436 | } 437 | return true 438 | } 439 | 440 | // 强制设定系统保留的API 441 | func (self *TP) ReserveAPI() Teleport{ 442 | // 添加保留规则——身份识别 443 | self.api[IDENTITY] = identi 444 | // 添加保留规则——忽略心跳请求 445 | self.api[HEARTBEAT] = beat 446 | 447 | return self 448 | } 449 | 450 | var identi, beat = new(identity), new(heartbeat) 451 | 452 | type identity struct{} 453 | 454 | func (*identity) Process(receive *NetData) *NetData { 455 | return nil 456 | } 457 | 458 | type heartbeat struct{} 459 | 460 | func (*heartbeat) Process(receive *NetData) *NetData { 461 | return nil 462 | } 463 | -------------------------------------------------------------------------------- /teleport/util.go: -------------------------------------------------------------------------------- 1 | package teleport 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "encoding/json" 7 | "fmt" 8 | "hash/crc32" 9 | "hash/fnv" 10 | "strconv" 11 | ) 12 | 13 | //string to hash 14 | func MakeHash(s string) string { 15 | const IEEE = 0xedb88320 16 | var IEEETable = crc32.MakeTable(IEEE) 17 | hash := fmt.Sprintf("%x", crc32.Checksum([]byte(s), IEEETable)) 18 | return hash 19 | } 20 | 21 | func HashString(encode string) uint64 { 22 | hash := fnv.New64() 23 | hash.Write([]byte(encode)) 24 | return hash.Sum64() 25 | } 26 | 27 | // 制作特征值方法一 28 | func MakeUnique(obj interface{}) string { 29 | baseString, _ := json.Marshal(obj) 30 | return strconv.FormatUint(HashString(string(baseString)), 10) 31 | } 32 | 33 | // 制作特征值方法二 34 | func MakeMd5(obj interface{}, length int) string { 35 | if length > 32 { 36 | length = 32 37 | } 38 | h := md5.New() 39 | baseString, _ := json.Marshal(obj) 40 | h.Write([]byte(baseString)) 41 | s := hex.EncodeToString(h.Sum(nil)) 42 | return s[:length] 43 | } 44 | --------------------------------------------------------------------------------