Browse Source

offline delay auto betting

master
tangmingyou 3 years ago
parent
commit
0d95077d5b
  1. 79
      api/poker.pb.go
  2. 1
      api/poker.proto
  3. 46
      api/proto.js
  4. 6
      internal/game/actions.go
  5. 13
      internal/game/player.go
  6. 36
      internal/game/table.go
  7. 3
      internal/letter/proto_writer.go
  8. 31
      internal/server/ws.go
  9. 6
      internal/service/auth.go
  10. 36
      internal/service/event/auto_betting.go
  11. 58
      internal/service/event/offline.go
  12. 27
      internal/service/event/online.go
  13. 37
      internal/service/online.go
  14. 79
      internal/service/store/store.go
  15. 78
      internal/service/table.go
  16. 7
      internal/session/net_account.go
  17. 12
      internal/session/net_client.go
  18. 19
      internal/session/player.go
  19. 47
      tool/watcher/watcher.go

79
api/poker.pb.go

@ -250,7 +250,7 @@ type ResFail struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // 401跳登录,402未加入牌桌,403挤号
Msg string `protobuf:"bytes,3,opt,name=msg,proto3" json:"msg,omitempty"` // 失败消息
}
@ -1264,7 +1264,6 @@ type TablePlayer struct {
Avatar string `protobuf:"bytes,4,opt,name=avatar,proto3" json:"avatar,omitempty"`
Chip int32 `protobuf:"varint,5,opt,name=chip,proto3" json:"chip,omitempty"` // 筹码
Status int32 `protobuf:"varint,6,opt,name=status,proto3" json:"status,omitempty"` // 玩家状态: 1待准备开始,2已准备开始,已开始(3等待其他玩家动作,4待大盲注,5待小盲注,6待跟注,7已弃)
LastStatus int32 `protobuf:"varint,7,opt,name=lastStatus,proto3" json:"lastStatus,omitempty"` // TODO 删, 前端通过消息监听获取下注金额信息
Master bool `protobuf:"varint,8,opt,name=master,proto3" json:"master,omitempty"` // 是否房主
RoundBetTimes int32 `protobuf:"varint,11,opt,name=roundBetTimes,proto3" json:"roundBetTimes,omitempty"` // 该局下注次数
TotalBetChip int32 `protobuf:"varint,13,opt,name=totalBetChip,proto3" json:"totalBetChip,omitempty"` // 该局牌已下注筹码
@ -1347,13 +1346,6 @@ func (x *TablePlayer) GetStatus() int32 {
return 0
}
func (x *TablePlayer) GetLastStatus() int32 {
if x != nil {
return x.LastStatus
}
return 0
}
func (x *TablePlayer) GetMaster() bool {
if x != nil {
return x.Master
@ -1702,6 +1694,7 @@ type ReqBetting struct {
BetType int32 `protobuf:"varint,1,opt,name=betType,proto3" json:"betType,omitempty"` // 1跟注,2加注或跟注(-[0]+),3All-In,4弃牌,5过牌
BetChip int32 `protobuf:"varint,2,opt,name=betChip,proto3" json:"betChip,omitempty"` // 跟注/加注金额
Operator int32 `protobuf:"varint,3,opt,name=operator,proto3" json:"operator,omitempty"` // 0玩家操作,1自动操作
}
func (x *ReqBetting) Reset() {
@ -1750,6 +1743,13 @@ func (x *ReqBetting) GetBetChip() int32 {
return 0
}
func (x *ReqBetting) GetOperator() int32 {
if x != nil {
return x.Operator
}
return 0
}
// 通知玩家某个其他玩家动作
type ResNoticePlayerLine struct {
state protoimpl.MessageState
@ -2059,7 +2059,7 @@ var file_poker_proto_rawDesc = []byte{
0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x61, 0x72, 0x64, 0x12, 0x2a, 0x0a, 0x07, 0x70,
0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x07,
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x22, 0x8f, 0x03, 0x0a, 0x0b, 0x54, 0x61, 0x62, 0x6c,
0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x22, 0xef, 0x02, 0x0a, 0x0b, 0x54, 0x61, 0x62, 0x6c,
0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x62, 0x6f, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x72, 0x6f, 0x62, 0x6f, 0x74, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a,
@ -2068,9 +2068,7 @@ var file_poker_proto_rawDesc = []byte{
0x74, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61,
0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x68, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,
0x04, 0x63, 0x68, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a,
0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
0x05, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a,
0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a,
0x06, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d,
0x61, 0x73, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x65,
0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x72, 0x6f,
@ -2101,36 +2099,37 @@ var file_poker_proto_rawDesc = []byte{
0x71, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65,
0x71, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c,
0x65, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x44, 0x69, 0x73, 0x6d, 0x69, 0x73, 0x73, 0x47,
0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x40, 0x0a, 0x0a, 0x52, 0x65, 0x71, 0x42,
0x61, 0x6d, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x5c, 0x0a, 0x0a, 0x52, 0x65, 0x71, 0x42,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28,
0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x22, 0x73, 0x0a, 0x13, 0x52, 0x65,
0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a,
0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69,
0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e,
0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x22,
0x3e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x53, 0x65, 0x74,
0x74, 0x6c, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x46,
0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x22,
0x92, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65,
0x72, 0x43, 0x68, 0x69, 0x70, 0x12, 0x40, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69,
0x70, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65,
0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69, 0x70, 0x2e,
0x57, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x77,
0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x1a, 0x3b, 0x0a, 0x0d, 0x57, 0x69, 0x6e, 0x73, 0x43,
0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x3a, 0x02, 0x38, 0x01, 0x22, 0x28, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x4e,
0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x0b,
0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x73, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x4e, 0x6f, 0x74,
0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a,
0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e,
0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x12,
0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x18, 0x0d,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x22, 0x3e, 0x0a, 0x10, 0x52,
0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x12,
0x2a, 0x0a, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x46, 0x75, 0x6c, 0x6c, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x11,
0x52, 0x65, 0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69,
0x70, 0x12, 0x40, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x18, 0x09, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x43, 0x61, 0x6c,
0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69, 0x70, 0x2e, 0x57, 0x69, 0x6e, 0x73,
0x43, 0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43,
0x68, 0x69, 0x70, 0x1a, 0x3b, 0x0a, 0x0d, 0x57, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x45,
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x22, 0x28, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x52,
0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x0b, 0x5a, 0x09, 0x2e, 0x2f,
0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

1
api/poker.proto

@ -191,6 +191,7 @@ message ResDismissGameTable {
message ReqBetting {
int32 betType = 1; // 1,2(-[0]+),3All-In,4,5
int32 betChip = 2; // /
int32 operator = 3; // 0,1
}
//

46
api/proto.js

@ -5265,7 +5265,6 @@ export const api = $root.api = (() => {
* @property {string|null} [avatar] TablePlayer avatar
* @property {number|null} [chip] TablePlayer chip
* @property {number|null} [status] TablePlayer status
* @property {number|null} [lastStatus] TablePlayer lastStatus
* @property {boolean|null} [master] TablePlayer master
* @property {number|null} [roundBetTimes] TablePlayer roundBetTimes
* @property {number|null} [totalBetChip] TablePlayer totalBetChip
@ -5338,14 +5337,6 @@ export const api = $root.api = (() => {
*/
TablePlayer.prototype.status = 0;
/**
* TablePlayer lastStatus.
* @member {number} lastStatus
* @memberof api.TablePlayer
* @instance
*/
TablePlayer.prototype.lastStatus = 0;
/**
* TablePlayer master.
* @member {boolean} master
@ -5430,8 +5421,6 @@ export const api = $root.api = (() => {
writer.uint32(/* id 5, wireType 0 =*/40).int32(message.chip);
if (message.status != null && Object.hasOwnProperty.call(message, "status"))
writer.uint32(/* id 6, wireType 0 =*/48).int32(message.status);
if (message.lastStatus != null && Object.hasOwnProperty.call(message, "lastStatus"))
writer.uint32(/* id 7, wireType 0 =*/56).int32(message.lastStatus);
if (message.master != null && Object.hasOwnProperty.call(message, "master"))
writer.uint32(/* id 8, wireType 0 =*/64).bool(message.master);
if (message.roundBetTimes != null && Object.hasOwnProperty.call(message, "roundBetTimes"))
@ -5503,10 +5492,6 @@ export const api = $root.api = (() => {
message.status = reader.int32();
break;
}
case 7: {
message.lastStatus = reader.int32();
break;
}
case 8: {
message.master = reader.bool();
break;
@ -5586,9 +5571,6 @@ export const api = $root.api = (() => {
if (message.status != null && message.hasOwnProperty("status"))
if (!$util.isInteger(message.status))
return "status: integer expected";
if (message.lastStatus != null && message.hasOwnProperty("lastStatus"))
if (!$util.isInteger(message.lastStatus))
return "lastStatus: integer expected";
if (message.master != null && message.hasOwnProperty("master"))
if (typeof message.master !== "boolean")
return "master: boolean expected";
@ -5651,8 +5633,6 @@ export const api = $root.api = (() => {
message.chip = object.chip | 0;
if (object.status != null)
message.status = object.status | 0;
if (object.lastStatus != null)
message.lastStatus = object.lastStatus | 0;
if (object.master != null)
message.master = Boolean(object.master);
if (object.roundBetTimes != null)
@ -5708,7 +5688,6 @@ export const api = $root.api = (() => {
object.avatar = "";
object.chip = 0;
object.status = 0;
object.lastStatus = 0;
object.master = false;
object.roundBetTimes = 0;
object.handType = null;
@ -5730,8 +5709,6 @@ export const api = $root.api = (() => {
object.chip = message.chip;
if (message.status != null && message.hasOwnProperty("status"))
object.status = message.status;
if (message.lastStatus != null && message.hasOwnProperty("lastStatus"))
object.lastStatus = message.lastStatus;
if (message.master != null && message.hasOwnProperty("master"))
object.master = message.master;
if (message.roundBetTimes != null && message.hasOwnProperty("roundBetTimes"))
@ -7078,6 +7055,7 @@ export const api = $root.api = (() => {
* @interface IReqBetting
* @property {number|null} [betType] ReqBetting betType
* @property {number|null} [betChip] ReqBetting betChip
* @property {number|null} [operator] ReqBetting operator
*/
/**
@ -7111,6 +7089,14 @@ export const api = $root.api = (() => {
*/
ReqBetting.prototype.betChip = 0;
/**
* ReqBetting operator.
* @member {number} operator
* @memberof api.ReqBetting
* @instance
*/
ReqBetting.prototype.operator = 0;
/**
* Creates a new ReqBetting instance using the specified properties.
* @function create
@ -7139,6 +7125,8 @@ export const api = $root.api = (() => {
writer.uint32(/* id 1, wireType 0 =*/8).int32(message.betType);
if (message.betChip != null && Object.hasOwnProperty.call(message, "betChip"))
writer.uint32(/* id 2, wireType 0 =*/16).int32(message.betChip);
if (message.operator != null && Object.hasOwnProperty.call(message, "operator"))
writer.uint32(/* id 3, wireType 0 =*/24).int32(message.operator);
return writer;
};
@ -7181,6 +7169,10 @@ export const api = $root.api = (() => {
message.betChip = reader.int32();
break;
}
case 3: {
message.operator = reader.int32();
break;
}
default:
reader.skipType(tag & 7);
break;
@ -7222,6 +7214,9 @@ export const api = $root.api = (() => {
if (message.betChip != null && message.hasOwnProperty("betChip"))
if (!$util.isInteger(message.betChip))
return "betChip: integer expected";
if (message.operator != null && message.hasOwnProperty("operator"))
if (!$util.isInteger(message.operator))
return "operator: integer expected";
return null;
};
@ -7241,6 +7236,8 @@ export const api = $root.api = (() => {
message.betType = object.betType | 0;
if (object.betChip != null)
message.betChip = object.betChip | 0;
if (object.operator != null)
message.operator = object.operator | 0;
return message;
};
@ -7260,11 +7257,14 @@ export const api = $root.api = (() => {
if (options.defaults) {
object.betType = 0;
object.betChip = 0;
object.operator = 0;
}
if (message.betType != null && message.hasOwnProperty("betType"))
object.betType = message.betType;
if (message.betChip != null && message.hasOwnProperty("betChip"))
object.betChip = message.betChip;
if (message.operator != null && message.hasOwnProperty("operator"))
object.operator = message.operator;
return object;
};

6
internal/game/actions.go

@ -6,6 +6,7 @@ import (
"github.com/golang/protobuf/proto"
"texas-poker-bk/api"
"texas-poker-bk/tool/collect"
"time"
)
type PlayerBetting struct {
@ -274,6 +275,11 @@ func SetNextPlayer4Limited(roundStart bool, t *Table, current *Player) {
}
// TODO 筹码不够跟注时可 all in
// 玩家离线,n秒后自动投注
if !nextP.Client.IsOnline() {
nextP.OfflineAutoBettingDelayKey = nextP.GameTable.
RefAutoBettingDelayQueue.Add(60*time.Second, nextP.Id)
}
}
func AllIn4NoLimited(player *Player, betChip int32) proto.Message {

13
internal/game/player.go

@ -3,6 +3,7 @@ package game
import (
"sync"
"texas-poker-bk/internal/letter"
"texas-poker-bk/tool/collect"
)
var (
@ -28,9 +29,11 @@ type Player struct {
RoundCheckRaiseOnly bool // 该回合仅过牌下注
GameTable *Table // 当前牌桌
ProtoWriter letter.ProtoWriter
Client letter.ProtoClient
Lock *sync.Mutex
StatusLock *sync.Mutex
OfflineAutoBettingDelayKey int64
}
func (p *Player) Init() {
@ -84,3 +87,11 @@ func (p *Player) SetStatus(status int32) {
p.Status = status
}
// AutoBetting 回合离线时自动操作
func (p *Player) AutoBetting() (BetType int32, BetChip int32) {
if collect.In(5, p.BetOpts...) {
return 5, 0
}
return 4, 0
}

36
internal/game/table.go

@ -8,6 +8,18 @@ import (
"texas-poker-bk/tool/collect"
)
var (
// LobbyTables 存储当前进行中的牌桌
// LobbyTables *store.Store[int32, *Table]
)
func init() {
//var tableZeroValue *Table = nil
//LobbyTables = store.NewStore(tableZeroValue, func(k int32) string {
// return strconv.Itoa(int(k))
//})
}
type Table struct {
TableNo int32 // 牌桌编号
MasterId int64 // 房主Id
@ -43,6 +55,8 @@ type Table struct {
Lock *sync.Mutex // 牌桌锁
PlayersLock *sync.Mutex
RefAutoBettingDelayQueue *collect.DelayQueue[int64]
}
// InitGameAndPlayerStatus 初始化桌面
@ -171,6 +185,20 @@ func (t *Table) JoinPlayer(player *Player) (int, error) {
return -1, errors.New("牌桌玩家已满")
}
// RemovePlayer 从牌桌移除玩家
func (t *Table) RemovePlayer(playerId int64) (found bool, player *Player) {
t.PlayersLock.Lock()
defer t.PlayersLock.Unlock()
// 从牌桌移除
for i, p := range t.Players {
if p.Id == playerId {
t.Players[i] = nil
return true, p
}
}
return false, nil
}
// BuildResGameFullStatus 构建牌桌当前游戏状态消息
func (t *Table) BuildResGameFullStatus() *api.ResGameFullStatus {
resGame := &api.ResGameFullStatus{
@ -240,7 +268,7 @@ func (t *Table) NoticeGameFullStatus() {
resGame := t.BuildResGameFullStatus()
gameEnd := collect.In(t.Stage, 1, 7, 9)
for _, player := range t.Players {
if player != nil && player.ProtoWriter != nil {
if player != nil && player.Client != nil {
resGame.PlayerId = player.Id
// 除结算时,只返回自己的手牌
if !gameEnd {
@ -253,7 +281,7 @@ func (t *Table) NoticeGameFullStatus() {
}
}
}
player.ProtoWriter.Write(resGame)
player.Client.Write(resGame)
}
}
}
@ -261,8 +289,8 @@ func (t *Table) NoticeGameFullStatus() {
// NoticeAllPlayer 发送消息到牌桌所有玩家
func (t *Table) NoticeAllPlayer(message proto.Message) {
for _, player := range t.Players {
if player != nil && player.ProtoWriter != nil {
player.ProtoWriter.Write(message)
if player != nil && player.Client != nil {
player.Client.Write(message)
}
}
}

3
internal/letter/proto_writer.go

@ -2,6 +2,7 @@ package letter
import "github.com/golang/protobuf/proto"
type ProtoWriter interface {
type ProtoClient interface {
IsOnline() bool
Write(message proto.Message)
}

31
internal/server/ws.go

@ -9,6 +9,7 @@ import (
"net/http"
"texas-poker-bk/api"
"texas-poker-bk/internal/conf"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/ip"
"time"
@ -35,17 +36,6 @@ func RouteWs(ctx *gin.Context) {
}
func Upgrade(ctx *gin.Context) {
//token, exists := ctx.GetQuery("t")
//if !exists {
// ctx.JSON(http.StatusUnauthorized, gin.H{"api": "unauth"})
// return
//}
//subject, err := service.DecodeSubject(token)
//if err != nil {
// ctx.JSON(http.StatusUnauthorized, gin.H{"api": "token failed"})
// return
//}
conn, err := upgradeWs(ctx.Writer, ctx.Request)
if err != nil {
fmt.Println(err)
@ -86,10 +76,15 @@ const (
// 处理新建的websocket
func handleNetClient(client *session.NetClient) {
defer func() {
// 捕获aes解析错误
// 捕获其他错误
if r := recover(); r != nil {
fmt.Println(r)
client.Write(&api.ResFail{Code: 401, Msg: r.(error).Error()})
fmt.Println("recover error: ", r)
client.Write(&api.ResFail{Code: 500, Msg: r.(error).Error()})
}
}()
defer func() {
if client.Account != nil {
event.OfflineWatcher.Add(client.Account.Id, true)
}
}()
// https://github.com/gorilla/websocket/blob/a68708917c6a4f06314ab4e52493cc61359c9d42/examples/chat/conn.go#L50
@ -104,7 +99,7 @@ func handleNetClient(client *session.NetClient) {
return client.Conn.SetReadDeadline(time.Now().Add(pongWait))
})
// TODO 1分钟后过期
// TODO 未认证连接30s后过期
for {
// 阻塞读取消息
_, bytes, err := client.Conn.ReadMessage()
@ -115,24 +110,28 @@ func handleNetClient(client *session.NetClient) {
} else {
// 读失败
log.Printf("ReadMessage error: %T, %v", err, err)
client.Close("read conn err:" + err.Error())
}
client.Write(&api.ResFail{Msg: "消息读失败:" + err.Error()})
client.Close("ws read err: " + err.Error())
return
}
wrap := &api.ProtoWrap{}
err = proto.Unmarshal(bytes, wrap)
if err != nil {
client.Write(&api.ResFail{Msg: "消息解析失败:" + err.Error()})
client.Close("api unmarshal fail! " + err.Error())
return
}
msg, err := api.NewProtoInstance(wrap.Op)
if err != nil {
client.WriteSeq(false, wrap.Seq, &api.ResFail{Msg: "消息体解析失败_1:" + err.Error()})
client.Close(err.Error())
return
}
err = proto.Unmarshal(wrap.Body, msg)
if err != nil {
client.WriteSeq(false, wrap.Seq, &api.ResFail{Msg: "消息体解析失败_2:" + err.Error()})
client.Close("api body unmarshal fail! " + err.Error())
return
}

6
internal/service/auth.go

@ -16,6 +16,7 @@ import (
"texas-poker-bk/internal/conf"
"texas-poker-bk/internal/dao"
"texas-poker-bk/internal/model/entity"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect"
@ -245,12 +246,15 @@ func HandleReqIdentity(client *session.NetClient, msg *api.ReqIdentity) (proto.M
current.Client.Write(&api.ResFail{Code: 403, Msg: "该账号在其他地方登录,您已下线"})
current.Client = account.Client
if current.Player != nil {
current.Player.ProtoWriter = account.Client
current.Player.Client = account.Client
}
account = current
}
client.Account = account
// 在线通知消息
event.OnlineWatcher.Add(account.Id, true)
// response
res := &api.ResIdentity{Id: subject.Id, Username: subject.Name, Avatar: subject.Avatar}
return res, nil

36
internal/service/event/auto_betting.go

@ -0,0 +1,36 @@
package event
import (
"fmt"
"github.com/golang/protobuf/proto"
"texas-poker-bk/api"
"texas-poker-bk/internal/game"
"texas-poker-bk/internal/service/store"
"texas-poker-bk/tool/collect"
"time"
)
var AutoBettingDelayQueue *collect.DelayQueue[int64]
var HandleReqBetting func(player *game.Player, msg *api.ReqBetting) (proto.Message, error)
func init() {
AutoBettingDelayQueue = collect.NewDelayQueue(handleAutoBetting)
}
func handleAutoBetting(accountId int64, _ time.Time) {
fmt.Println("auto betting...", accountId)
account := store.NetAccounts.Get(accountId)
if account == nil || account.Player == nil || account.Player.Status != 6 {
return
}
// 自动下注逻辑
betType, betChip := account.Player.AutoBetting()
res, err := HandleReqBetting(account.Player, &api.ReqBetting{
BetType: betType, BetChip: betChip, Operator: 1,
})
resFail, ok := res.(*api.ResFail)
if err != nil || ok {
fmt.Println("auto betting err: ", resFail, err)
}
}

58
internal/service/event/offline.go

@ -0,0 +1,58 @@
package event
import (
"fmt"
"texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect"
"texas-poker-bk/tool/watcher"
"time"
)
var (
// OfflineWatcher 离线动作
OfflineWatcher *watcher.Watcher[int64, bool]
//OfflineQueue *collect.DelayQueue[*session.NetAccount]
)
func init() {
OfflineWatcher = watcher.NewWatcher(128, handleOffline)
go OfflineWatcher.Run()
}
func handleOffline(e *watcher.Event[int64, bool]) {
account := store.NetAccounts.Get(e.Publisher)
if account == nil {
return
}
fmt.Printf("account %d, %s handle offline.\n", account.Id, account.UserName)
if account == nil || store.NetAccounts.Get(account.Id) == nil {
return
}
if account.Player == nil {
// 不在游戏中,直接下线
removeMemoryAccount(account)
return
}
if collect.In(account.Player.GameTable.Stage, 1, 9) {
// 牌局未在进行中直接移除玩家并下线
account.Player.GameTable.RemovePlayer(account.Id)
account.SettlePlayerChip()
removeMemoryAccount(account)
return
}
// 判断当前是玩家回合(60s自动过牌/弃牌)
if account.Player.Status == 6 {
account.Player.GameTable.RefAutoBettingDelayQueue.Add(60*time.Second, account.Id)
}
// 待玩家回合时还是offline(自动过牌/弃牌)
}
func removeMemoryAccount(account *session.NetAccount) {
store.NetAccounts.Delete(account.Id)
store.TokenVersions.Delete(account.Id)
// TODO 持久化账户金额
}

27
internal/service/event/online.go

@ -0,0 +1,27 @@
package event
import (
"texas-poker-bk/internal/service/store"
"texas-poker-bk/tool/watcher"
)
// OnlineWatcher 上线动作
var OnlineWatcher *watcher.Watcher[int64, bool]
func init() {
OnlineWatcher = watcher.NewWatcher(128, handleOnline)
go OnlineWatcher.Run()
}
// handleOnline 玩家上线了,取消一些自动操作动作
func handleOnline(e *watcher.Event[int64, bool]) {
account := store.NetAccounts.Get(e.Publisher)
if account == nil || account.Player == nil {
return
}
// 取消自动下注
if account.Player.OfflineAutoBettingDelayKey != 0 {
AutoBettingDelayQueue.Cancel(account.Player.OfflineAutoBettingDelayKey)
account.Player.OfflineAutoBettingDelayKey = 0
}
}

37
internal/service/online.go

@ -1,37 +0,0 @@
package service
import (
"fmt"
"texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect"
"time"
)
func init() {
OfflineQueue := collect.NewDelayQueue(HandleUserOffline)
OfflineQueue.Add(1, nil)
}
func HandleUserOffline(account *session.NetAccount, t time.Time) {
fmt.Printf("account %d, %s handle offline.\n", account.Id, account.UserName)
if account == nil || store.NetAccounts.Get(account.Id) == nil {
return
}
if account.Player == nil {
// 不在游戏中,直接下线
removeOnlineUser(account)
return
}
if collect.In(account.Player.GameTable.Stage, 1, 9) {
// TODO account.Player.GameTable.RemovePlayer()
}
}
func removeOnlineUser(account *session.NetAccount) {
store.NetAccounts.Delete(account.Id)
store.TokenVersions.Delete(account.Id)
// TODO 持久化账户金额
}

79
internal/service/store/store.go

@ -12,12 +12,14 @@ import (
var (
TableNo *atomic.Int32
LobbyTables *store[int32, *game.Table]
// LobbyTables 存储当前进行中的牌桌
LobbyTables *Store[int32, *game.Table]
// NetAccounts 存储一下在线用户, TODO 连接终端,未在牌局或牌局未开始中回收账号,机器人代打后回收账号
NetAccounts *store[int64, *session.NetAccount]
NetAccounts *Store[int64, *session.NetAccount]
TokenVersions *store[int64, int32]
// TokenVersions 存储token版本号,登录时+1,一个账户同一时间只允许一个token有效
TokenVersions *Store[int64, int32]
)
const (
@ -25,21 +27,51 @@ const (
DefaultExpiration time.Duration = cache.DefaultExpiration
)
type store[K any, V any] struct {
func init() {
TableNo = &atomic.Int32{} // 牌桌编号计数器
var tableZeroValue *game.Table = nil
LobbyTables = NewStore(tableZeroValue, func(k int32) string {
return strconv.Itoa(int(k))
})
var accountZeroValue *session.NetAccount = nil
NetAccounts = NewStore(accountZeroValue, func(k int64) string {
return strconv.FormatInt(k, 10)
})
TokenVersions = NewStore(int32(-1), func(k int64) string {
return strconv.FormatInt(k, 10)
})
}
type Store[K any, V any] struct {
c *cache.Cache
zeroValue V
k2str func(K) string
}
func (s *store[K, V]) SetDefault(k K, v V) {
func NewStore[K any, V any](zeroValue V, k2str func(K) string) *Store[K, V] {
return NewExpireStore(cache.NoExpiration, cache.NoExpiration, zeroValue, k2str)
}
func NewExpireStore[K any, V any](defaultExpiration, cleanupInterval time.Duration, zeroValue V, k2str func(K) string) *Store[K, V] {
return &Store[K, V]{
c: cache.New(defaultExpiration, cleanupInterval),
zeroValue: zeroValue,
k2str: k2str,
}
}
func (s *Store[K, V]) SetDefault(k K, v V) {
s.c.SetDefault(s.k2str(k), v)
}
func (s *store[K, V]) Set(k K, v V, d time.Duration) {
func (s *Store[K, V]) Set(k K, v V, d time.Duration) {
s.c.Set(s.k2str(k), v, d)
}
func (s *store[K, V]) Get(k K) V {
func (s *Store[K, V]) Get(k K) V {
v, found := s.c.Get(s.k2str(k))
if !found {
return s.zeroValue
@ -47,43 +79,16 @@ func (s *store[K, V]) Get(k K) V {
return v.(V)
}
func (s *store[K, V]) Delete(k K) {
func (s *Store[K, V]) Delete(k K) {
s.c.Delete(s.k2str(k))
}
func (s *store[K, V]) ForEach(f func(k string, v V)) {
func (s *Store[K, V]) ForEach(f func(k string, v V)) {
for k, v := range s.c.Items() {
f(k, v.Object.(V))
}
}
func (s *store[K, V]) Count() int {
func (s *Store[K, V]) Count() int {
return s.c.ItemCount()
}
func init() {
LobbyTables = &store[int32, *game.Table]{
c: cache.New(cache.NoExpiration, cache.NoExpiration),
zeroValue: nil,
k2str: func(k int32) string {
return strconv.Itoa(int(k))
},
}
NetAccounts = &store[int64, *session.NetAccount]{
c: cache.New(cache.NoExpiration, cache.NoExpiration),
zeroValue: nil,
k2str: func(k int64) string {
return strconv.FormatInt(k, 10)
},
}
TokenVersions = &store[int64, int32]{
c: cache.New(cache.NoExpiration, cache.NoExpiration),
zeroValue: -1,
k2str: func(k int64) string {
return strconv.FormatInt(k, 10)
},
}
TableNo = &atomic.Int32{} // 牌桌编号计数器
}

78
internal/service/table.go

@ -7,11 +7,17 @@ import (
"sort"
"texas-poker-bk/api"
"texas-poker-bk/internal/game"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect"
)
func init() {
// TODO 循环依赖 ioc 参考
event.HandleReqBetting = HandleReqBetting
}
// HandleReqCreateTable 创建桌面
func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable) (proto.Message, error) {
account.Lock.Lock()
@ -45,6 +51,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
SmallBlindChip: msg.BigBlind / 2,
LimitInAmount: msg.BigBlind * 100,
TexasType: msg.TexasType,
RefAutoBettingDelayQueue: event.AutoBettingDelayQueue,
}
// 游戏类型 对应 下注处理流程
switch msg.TexasType {
@ -69,7 +76,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
TotalBetChip: 0,
Cards: [2]*game.Card{},
GameTable: table,
ProtoWriter: account.Client,
Client: account.Client,
}
owner.Init()
// 挂载 player 到 account 上
@ -89,7 +96,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
}
// HandleReqLobbyView 返回当前所有桌面和玩家数量
func HandleReqLobbyView(account *session.NetAccount, msg *api.ReqLobbyView) (proto.Message, error) {
func HandleReqLobbyView(account *session.NetAccount, _ *api.ReqLobbyView) (proto.Message, error) {
res := &api.ResLobbyView{}
if store.LobbyTables.Count() == 0 {
return res, nil
@ -119,7 +126,7 @@ func HandleReqLobbyView(account *session.NetAccount, msg *api.ReqLobbyView) (pro
}
}
if collect.IsNotEmptySlice(t.Robots) {
for _, _ = range t.Robots {
for range t.Robots {
table.Players[playerIdx] = &api.LobbyPlayer{Robot: true}
playerIdx++
}
@ -171,7 +178,7 @@ func HandleReqJoinTable(account *session.NetAccount, msg *api.ReqJoinTable) (pro
TotalBetChip: 0,
Cards: [2]*game.Card{nil, nil},
GameTable: table,
ProtoWriter: account.Client,
Client: account.Client,
}
player.Init()
index, err := table.JoinPlayer(player)
@ -205,17 +212,13 @@ func HandleReqKickOutTable(player *game.Player, msg *api.ReqKickOutTable) (proto
if player.GameTable.Stage != 1 {
return &api.ResFail{Msg: "牌局进行中不能踢人"}, nil
}
for i, p := range player.GameTable.Players {
if p != nil && p.Id == msg.PlayerId {
// 从牌桌移除该玩家
player.GameTable.Players[i] = nil
// 被对踢出人发送消息
account := store.NetAccounts.Get(p.Id)
account.Player = nil // 解除账户绑定
account.IncrementBalance(player.Chip) // 筹码返还账户
p.ProtoWriter.Write(&api.ResKickOutTable{}) // 被踢玩家消息
break
}
found, p := player.GameTable.RemovePlayer(msg.PlayerId)
if found {
// 结算玩家金额
store.NetAccounts.Get(p.Id).SettlePlayerChip()
// 被踢玩家消息
player.Client.Write(&api.ResKickOutTable{})
}
// 通知牌桌所有玩家
player.GameTable.NoticeGameFullStatus()
@ -223,7 +226,7 @@ func HandleReqKickOutTable(player *game.Player, msg *api.ReqKickOutTable) (proto
}
// HandleReqLeaveTable 离开桌面
func HandleReqLeaveTable(player *game.Player, msg *api.ReqLeaveTable) (proto.Message, error) {
func HandleReqLeaveTable(player *game.Player, _ *api.ReqLeaveTable) (proto.Message, error) {
player.GameTable.PlayersLock.Lock()
defer player.GameTable.PlayersLock.Unlock()
@ -237,16 +240,10 @@ func HandleReqLeaveTable(player *game.Player, msg *api.ReqLeaveTable) (proto.Mes
return &api.ResFail{Msg: "房主不能退出,请解散房间"}, nil
}
// 从牌桌移除
for i, p := range player.GameTable.Players {
if p.Id == player.Id {
player.GameTable.Players[i] = nil
break
found, p := player.GameTable.RemovePlayer(player.Id)
if found {
store.NetAccounts.Get(p.Id).SettlePlayerChip()
}
}
account := store.NetAccounts.Get(player.Id)
account.Player = nil // 解除账户绑定
account.IncrementBalance(player.Chip) // 筹码返还账户
// 通知牌桌其他玩家
player.GameTable.NoticeGameFullStatus()
@ -254,7 +251,7 @@ func HandleReqLeaveTable(player *game.Player, msg *api.ReqLeaveTable) (proto.Mes
}
// HandleReqGameFullStatus 获取牌桌当前所有状态
func HandleReqGameFullStatus(player *game.Player, msg *api.ReqGameFullStatus) (proto.Message, error) {
func HandleReqGameFullStatus(player *game.Player, _ *api.ReqGameFullStatus) (proto.Message, error) {
if player.GameTable == nil {
return &api.ResFail{Msg: "当前未加入牌桌"}, nil
}
@ -274,7 +271,7 @@ func HandleReqGameFullStatus(player *game.Player, msg *api.ReqGameFullStatus) (p
}
// HandleReqReadyStart 准备开始游戏
func HandleReqReadyStart(player *game.Player, msg *api.ReqReadyStart) (proto.Message, error) {
func HandleReqReadyStart(player *game.Player, _ *api.ReqReadyStart) (proto.Message, error) {
player.Lock.Lock()
defer player.Lock.Unlock()
if player.Id == player.GameTable.MasterId {
@ -297,9 +294,11 @@ func HandleReqReadyStart(player *game.Player, msg *api.ReqReadyStart) (proto.Mes
return &api.ResSuccess{}, nil
}
func HandleReqCancelReady(player *game.Player, msg *api.ReqCancelReady) (proto.Message, error) {
player.Lock.Lock()
defer player.Lock.Unlock()
func HandleReqCancelReady(player *game.Player, _ *api.ReqCancelReady) (proto.Message, error) {
// 和 GameStart 互斥
player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock()
if player.Id == player.GameTable.MasterId {
return &api.ResFail{Msg: "房主不用准备"}, nil
}
@ -312,7 +311,7 @@ func HandleReqCancelReady(player *game.Player, msg *api.ReqCancelReady) (proto.M
}
// HandleReqDismissGameTable 解散牌桌
func HandleReqDismissGameTable(player *game.Player, msg *api.ReqDismissGameTable) (proto.Message, error) {
func HandleReqDismissGameTable(player *game.Player, _ *api.ReqDismissGameTable) (proto.Message, error) {
player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock()
if !collect.In(player.GameTable.Stage, 1, 7) {
@ -330,23 +329,16 @@ func HandleReqDismissGameTable(player *game.Player, msg *api.ReqDismissGameTable
account.Player = nil
player.GameTable.Players[i] = nil
if p.Id != player.Id {
p.ProtoWriter.Write(&api.ResDismissGameTable{})
p.Client.Write(&api.ResDismissGameTable{})
}
}
return &api.ResDismissGameTable{}, nil
}
// HandleReqGameStart 游戏开始,初始化,扣除大小盲注,每个玩家发2张牌
func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Message, error) {
func HandleReqGameStart(player *game.Player, _ *api.ReqGameStart) (proto.Message, error) {
player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock()
// 锁定牌桌所有玩家
for _, p := range player.GameTable.Players {
if p != nil {
p.Lock.Lock()
defer p.Lock.Unlock()
}
}
table := player.GameTable
if player.Id != table.MasterId {
@ -355,6 +347,9 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa
if !collect.In(table.Stage, 1, 7) {
return &api.ResFail{Msg: fmt.Sprintf("牌局状态有误%d", table.Stage)}, nil
}
if table.PlayerCount() < 2 {
return &api.ResFail{Msg: "至少需两位玩家可开始"}, nil
}
// 检查所有玩家准备状态
for _, p := range table.Players {
if p != nil && p.Id != table.MasterId && p.Status != 2 {
@ -434,6 +429,9 @@ func HandleReqGameStart(player *game.Player, msg *api.ReqGameStart) (proto.Messa
// HandleReqBetting 下注
func HandleReqBetting(player *game.Player, msg *api.ReqBetting) (proto.Message, error) {
player.Lock.Lock()
defer player.Lock.Unlock()
if player.Status != 6 {
return &api.ResFail{Msg: "当前未轮到您下注"}, nil
}

7
internal/session/net_account.go

@ -37,3 +37,10 @@ func (account *NetAccount) GetBalance() int32 {
defer account.BalanceLock.RUnlock()
return account.Balance
}
func (account *NetAccount) SettlePlayerChip() {
// 返还玩家账户金额
account.IncrementBalance(account.Player.Chip) // 牌桌筹码返还到账户 TODO 持久化
account.Player.Chip = 0
account.Player = nil // 解除账户绑定
}

12
internal/session/net_client.go

@ -6,6 +6,7 @@ import (
"github.com/gorilla/websocket"
"log"
"sync"
"sync/atomic"
"texas-poker-bk/api"
"time"
)
@ -14,24 +15,35 @@ import (
type NetClient struct {
// Id int64
Conn *websocket.Conn
Online *atomic.Bool
Account *NetAccount
WriteLock *sync.Mutex
}
func NewNetClient(conn *websocket.Conn) *NetClient {
online := &atomic.Bool{}
online.Store(true)
return &NetClient{
Conn: conn,
WriteLock: new(sync.Mutex),
Online: online,
}
}
func (c *NetClient) Close(reason string) {
log.Println("close NetClient reason: ", reason)
c.Online.Store(false)
err := c.Conn.Close()
if err != nil {
log.Println("Close Error", err)
}
}
func (c *NetClient) IsOnline() bool {
return c.Online.Load()
}
func (c *NetClient) Write(msg proto.Message) {
c.WriteSeq(true, 0, msg)
}

19
internal/session/player.go

@ -1,19 +0,0 @@
package session
import (
"sync"
"texas-poker-bk/internal/game"
)
// Player 玩家: table,balance,(hand card)
type Player struct {
Account *NetAccount
Id int64
Username string
Avatar string
Cards [2]*game.Card // 手牌
Chip int // 玩家筹码
Status int32 // 状态: 1待准备开始,2已准备开始,3等待其他玩家动作,4待大盲注,5待小盲注,6待跟注,7已弃
StatusLock *sync.Mutex
}

47
tool/watcher/watcher.go

@ -0,0 +1,47 @@
package watcher
import (
"time"
)
type Event[P any, T any] struct {
Publisher P
Payload T
UnixMilli int64
}
type Watcher[P any, T any] struct {
events chan *Event[P, T]
stop chan bool
handler func(event *Event[P, T])
}
func NewWatcher[P any, T any](buffer int, handler func(event *Event[P, T])) *Watcher[P, T] {
return &Watcher[P, T]{
events: make(chan *Event[P, T], buffer),
stop: make(chan bool),
handler: handler,
}
}
func (w *Watcher[P, T]) Run() {
for {
select {
case e := <-w.events:
w.handler(e)
case <-w.stop:
return
}
}
}
func (w *Watcher[P, T]) Stop() {
w.stop <- true
}
func (w *Watcher[P, T]) Add(publisher P, event T) {
e := &Event[P, T]{publisher, event, time.Now().UnixMilli()}
go func() {
w.events <- e
}()
}
Loading…
Cancel
Save