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 sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields 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"` // 失败消息 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"` Avatar string `protobuf:"bytes,4,opt,name=avatar,proto3" json:"avatar,omitempty"`
Chip int32 `protobuf:"varint,5,opt,name=chip,proto3" json:"chip,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已弃) 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"` // 是否房主 Master bool `protobuf:"varint,8,opt,name=master,proto3" json:"master,omitempty"` // 是否房主
RoundBetTimes int32 `protobuf:"varint,11,opt,name=roundBetTimes,proto3" json:"roundBetTimes,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"` // 该局牌已下注筹码 TotalBetChip int32 `protobuf:"varint,13,opt,name=totalBetChip,proto3" json:"totalBetChip,omitempty"` // 该局牌已下注筹码
@ -1347,13 +1346,6 @@ func (x *TablePlayer) GetStatus() int32 {
return 0 return 0
} }
func (x *TablePlayer) GetLastStatus() int32 {
if x != nil {
return x.LastStatus
}
return 0
}
func (x *TablePlayer) GetMaster() bool { func (x *TablePlayer) GetMaster() bool {
if x != nil { if x != nil {
return x.Master 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过牌 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"` // 跟注/加注金额 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() { func (x *ReqBetting) Reset() {
@ -1750,6 +1743,13 @@ func (x *ReqBetting) GetBetChip() int32 {
return 0 return 0
} }
func (x *ReqBetting) GetOperator() int32 {
if x != nil {
return x.Operator
}
return 0
}
// 通知玩家某个其他玩家动作 // 通知玩家某个其他玩家动作
type ResNoticePlayerLine struct { type ResNoticePlayerLine struct {
state protoimpl.MessageState 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 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, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x05, 0x52, 0x07, 0x62, 0x65, 0x74, 0x43, 0x68, 0x69, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x70,
0x73, 0x4e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6f, 0x70,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x73, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x4e, 0x6f, 0x74,
0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x69, 0x63, 0x65, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a,
0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52,
0x6e, 0x65, 0x31, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x08, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e,
0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x31, 0x12,
0x65, 0x33, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x22, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
0x3e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x53, 0x65, 0x74, 0x6c, 0x69, 0x6e, 0x65, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x18, 0x0d,
0x74, 0x6c, 0x65, 0x12, 0x2a, 0x0a, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x33, 0x22, 0x3e, 0x0a, 0x10, 0x52,
0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x46, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x53, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x12,
0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
0x92, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x46, 0x75, 0x6c, 0x6c, 0x53,
0x72, 0x43, 0x68, 0x69, 0x70, 0x12, 0x40, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x04, 0x67, 0x61, 0x6d, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x11,
0x70, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x52, 0x65, 0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69,
0x73, 0x43, 0x61, 0x6c, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69, 0x70, 0x2e, 0x70, 0x12, 0x40, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x18, 0x09, 0x20,
0x57, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x77, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x43, 0x61, 0x6c,
0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x1a, 0x3b, 0x0a, 0x0d, 0x57, 0x69, 0x6e, 0x73, 0x43, 0x63, 0x57, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x43, 0x68, 0x69, 0x70, 0x2e, 0x57, 0x69, 0x6e, 0x73,
0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x43, 0x68, 0x69, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x77, 0x69, 0x6e, 0x73, 0x43,
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x68, 0x69, 0x70, 0x1a, 0x3b, 0x0a, 0x0d, 0x57, 0x69, 0x6e, 0x73, 0x43, 0x68, 0x69, 0x70, 0x45,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x3a, 0x02, 0x38, 0x01, 0x22, 0x28, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x4e, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x65, 0x78, 0x74, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x42, 0x0b, 0x22, 0x28, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x47, 0x61, 0x6d, 0x65, 0x4e, 0x65, 0x78, 0x74, 0x52,
0x5a, 0x09, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
0x74, 0x6f, 0x33, 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 ( var (

1
api/poker.proto

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

6
internal/game/actions.go

@ -6,6 +6,7 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"texas-poker-bk/api" "texas-poker-bk/api"
"texas-poker-bk/tool/collect" "texas-poker-bk/tool/collect"
"time"
) )
type PlayerBetting struct { type PlayerBetting struct {
@ -274,6 +275,11 @@ func SetNextPlayer4Limited(roundStart bool, t *Table, current *Player) {
} }
// TODO 筹码不够跟注时可 all in // 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 { func AllIn4NoLimited(player *Player, betChip int32) proto.Message {

13
internal/game/player.go

@ -3,6 +3,7 @@ package game
import ( import (
"sync" "sync"
"texas-poker-bk/internal/letter" "texas-poker-bk/internal/letter"
"texas-poker-bk/tool/collect"
) )
var ( var (
@ -28,9 +29,11 @@ type Player struct {
RoundCheckRaiseOnly bool // 该回合仅过牌下注 RoundCheckRaiseOnly bool // 该回合仅过牌下注
GameTable *Table // 当前牌桌 GameTable *Table // 当前牌桌
ProtoWriter letter.ProtoWriter Client letter.ProtoClient
Lock *sync.Mutex Lock *sync.Mutex
StatusLock *sync.Mutex StatusLock *sync.Mutex
OfflineAutoBettingDelayKey int64
} }
func (p *Player) Init() { func (p *Player) Init() {
@ -84,3 +87,11 @@ func (p *Player) SetStatus(status int32) {
p.Status = status 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" "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 { type Table struct {
TableNo int32 // 牌桌编号 TableNo int32 // 牌桌编号
MasterId int64 // 房主Id MasterId int64 // 房主Id
@ -43,6 +55,8 @@ type Table struct {
Lock *sync.Mutex // 牌桌锁 Lock *sync.Mutex // 牌桌锁
PlayersLock *sync.Mutex PlayersLock *sync.Mutex
RefAutoBettingDelayQueue *collect.DelayQueue[int64]
} }
// InitGameAndPlayerStatus 初始化桌面 // InitGameAndPlayerStatus 初始化桌面
@ -171,6 +185,20 @@ func (t *Table) JoinPlayer(player *Player) (int, error) {
return -1, errors.New("牌桌玩家已满") 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 构建牌桌当前游戏状态消息 // BuildResGameFullStatus 构建牌桌当前游戏状态消息
func (t *Table) BuildResGameFullStatus() *api.ResGameFullStatus { func (t *Table) BuildResGameFullStatus() *api.ResGameFullStatus {
resGame := &api.ResGameFullStatus{ resGame := &api.ResGameFullStatus{
@ -240,7 +268,7 @@ func (t *Table) NoticeGameFullStatus() {
resGame := t.BuildResGameFullStatus() resGame := t.BuildResGameFullStatus()
gameEnd := collect.In(t.Stage, 1, 7, 9) gameEnd := collect.In(t.Stage, 1, 7, 9)
for _, player := range t.Players { for _, player := range t.Players {
if player != nil && player.ProtoWriter != nil { if player != nil && player.Client != nil {
resGame.PlayerId = player.Id resGame.PlayerId = player.Id
// 除结算时,只返回自己的手牌 // 除结算时,只返回自己的手牌
if !gameEnd { 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 发送消息到牌桌所有玩家 // NoticeAllPlayer 发送消息到牌桌所有玩家
func (t *Table) NoticeAllPlayer(message proto.Message) { func (t *Table) NoticeAllPlayer(message proto.Message) {
for _, player := range t.Players { for _, player := range t.Players {
if player != nil && player.ProtoWriter != nil { if player != nil && player.Client != nil {
player.ProtoWriter.Write(message) player.Client.Write(message)
} }
} }
} }

3
internal/letter/proto_writer.go

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

31
internal/server/ws.go

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"texas-poker-bk/api" "texas-poker-bk/api"
"texas-poker-bk/internal/conf" "texas-poker-bk/internal/conf"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/session" "texas-poker-bk/internal/session"
"texas-poker-bk/tool/ip" "texas-poker-bk/tool/ip"
"time" "time"
@ -35,17 +36,6 @@ func RouteWs(ctx *gin.Context) {
} }
func Upgrade(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) conn, err := upgradeWs(ctx.Writer, ctx.Request)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -86,10 +76,15 @@ const (
// 处理新建的websocket // 处理新建的websocket
func handleNetClient(client *session.NetClient) { func handleNetClient(client *session.NetClient) {
defer func() { defer func() {
// 捕获aes解析错误 // 捕获其他错误
if r := recover(); r != nil { if r := recover(); r != nil {
fmt.Println(r) fmt.Println("recover error: ", r)
client.Write(&api.ResFail{Code: 401, Msg: r.(error).Error()}) 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 // 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)) return client.Conn.SetReadDeadline(time.Now().Add(pongWait))
}) })
// TODO 1分钟后过期 // TODO 未认证连接30s后过期
for { for {
// 阻塞读取消息 // 阻塞读取消息
_, bytes, err := client.Conn.ReadMessage() _, bytes, err := client.Conn.ReadMessage()
@ -115,24 +110,28 @@ func handleNetClient(client *session.NetClient) {
} else { } else {
// 读失败 // 读失败
log.Printf("ReadMessage error: %T, %v", err, err) 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 return
} }
wrap := &api.ProtoWrap{} wrap := &api.ProtoWrap{}
err = proto.Unmarshal(bytes, wrap) err = proto.Unmarshal(bytes, wrap)
if err != nil { if err != nil {
client.Write(&api.ResFail{Msg: "消息解析失败:" + err.Error()})
client.Close("api unmarshal fail! " + err.Error()) client.Close("api unmarshal fail! " + err.Error())
return return
} }
msg, err := api.NewProtoInstance(wrap.Op) msg, err := api.NewProtoInstance(wrap.Op)
if err != nil { if err != nil {
client.WriteSeq(false, wrap.Seq, &api.ResFail{Msg: "消息体解析失败_1:" + err.Error()})
client.Close(err.Error()) client.Close(err.Error())
return return
} }
err = proto.Unmarshal(wrap.Body, msg) err = proto.Unmarshal(wrap.Body, msg)
if err != nil { if err != nil {
client.WriteSeq(false, wrap.Seq, &api.ResFail{Msg: "消息体解析失败_2:" + err.Error()})
client.Close("api body unmarshal fail! " + err.Error()) client.Close("api body unmarshal fail! " + err.Error())
return return
} }

6
internal/service/auth.go

@ -16,6 +16,7 @@ import (
"texas-poker-bk/internal/conf" "texas-poker-bk/internal/conf"
"texas-poker-bk/internal/dao" "texas-poker-bk/internal/dao"
"texas-poker-bk/internal/model/entity" "texas-poker-bk/internal/model/entity"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/service/store" "texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session" "texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect" "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.Write(&api.ResFail{Code: 403, Msg: "该账号在其他地方登录,您已下线"})
current.Client = account.Client current.Client = account.Client
if current.Player != nil { if current.Player != nil {
current.Player.ProtoWriter = account.Client current.Player.Client = account.Client
} }
account = current account = current
} }
client.Account = account client.Account = account
// 在线通知消息
event.OnlineWatcher.Add(account.Id, true)
// response // response
res := &api.ResIdentity{Id: subject.Id, Username: subject.Name, Avatar: subject.Avatar} res := &api.ResIdentity{Id: subject.Id, Username: subject.Name, Avatar: subject.Avatar}
return res, nil 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 ( var (
TableNo *atomic.Int32 TableNo *atomic.Int32
LobbyTables *store[int32, *game.Table] // LobbyTables 存储当前进行中的牌桌
LobbyTables *Store[int32, *game.Table]
// NetAccounts 存储一下在线用户, TODO 连接终端,未在牌局或牌局未开始中回收账号,机器人代打后回收账号 // 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 ( const (
@ -25,21 +27,51 @@ const (
DefaultExpiration time.Duration = cache.DefaultExpiration 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 c *cache.Cache
zeroValue V zeroValue V
k2str func(K) string 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) 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) 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)) v, found := s.c.Get(s.k2str(k))
if !found { if !found {
return s.zeroValue return s.zeroValue
@ -47,43 +79,16 @@ func (s *store[K, V]) Get(k K) V {
return v.(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)) 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() { for k, v := range s.c.Items() {
f(k, v.Object.(V)) f(k, v.Object.(V))
} }
} }
func (s *store[K, V]) Count() int { func (s *Store[K, V]) Count() int {
return s.c.ItemCount() 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" "sort"
"texas-poker-bk/api" "texas-poker-bk/api"
"texas-poker-bk/internal/game" "texas-poker-bk/internal/game"
"texas-poker-bk/internal/service/event"
"texas-poker-bk/internal/service/store" "texas-poker-bk/internal/service/store"
"texas-poker-bk/internal/session" "texas-poker-bk/internal/session"
"texas-poker-bk/tool/collect" "texas-poker-bk/tool/collect"
) )
func init() {
// TODO 循环依赖 ioc 参考
event.HandleReqBetting = HandleReqBetting
}
// HandleReqCreateTable 创建桌面 // HandleReqCreateTable 创建桌面
func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable) (proto.Message, error) { func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable) (proto.Message, error) {
account.Lock.Lock() account.Lock.Lock()
@ -45,6 +51,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
SmallBlindChip: msg.BigBlind / 2, SmallBlindChip: msg.BigBlind / 2,
LimitInAmount: msg.BigBlind * 100, LimitInAmount: msg.BigBlind * 100,
TexasType: msg.TexasType, TexasType: msg.TexasType,
RefAutoBettingDelayQueue: event.AutoBettingDelayQueue,
} }
// 游戏类型 对应 下注处理流程 // 游戏类型 对应 下注处理流程
switch msg.TexasType { switch msg.TexasType {
@ -69,7 +76,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
TotalBetChip: 0, TotalBetChip: 0,
Cards: [2]*game.Card{}, Cards: [2]*game.Card{},
GameTable: table, GameTable: table,
ProtoWriter: account.Client, Client: account.Client,
} }
owner.Init() owner.Init()
// 挂载 player 到 account 上 // 挂载 player 到 account 上
@ -89,7 +96,7 @@ func HandleReqCreateTable(account *session.NetAccount, msg *api.ReqCreateTable)
} }
// HandleReqLobbyView 返回当前所有桌面和玩家数量 // 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{} res := &api.ResLobbyView{}
if store.LobbyTables.Count() == 0 { if store.LobbyTables.Count() == 0 {
return res, nil return res, nil
@ -119,7 +126,7 @@ func HandleReqLobbyView(account *session.NetAccount, msg *api.ReqLobbyView) (pro
} }
} }
if collect.IsNotEmptySlice(t.Robots) { if collect.IsNotEmptySlice(t.Robots) {
for _, _ = range t.Robots { for range t.Robots {
table.Players[playerIdx] = &api.LobbyPlayer{Robot: true} table.Players[playerIdx] = &api.LobbyPlayer{Robot: true}
playerIdx++ playerIdx++
} }
@ -171,7 +178,7 @@ func HandleReqJoinTable(account *session.NetAccount, msg *api.ReqJoinTable) (pro
TotalBetChip: 0, TotalBetChip: 0,
Cards: [2]*game.Card{nil, nil}, Cards: [2]*game.Card{nil, nil},
GameTable: table, GameTable: table,
ProtoWriter: account.Client, Client: account.Client,
} }
player.Init() player.Init()
index, err := table.JoinPlayer(player) index, err := table.JoinPlayer(player)
@ -205,17 +212,13 @@ func HandleReqKickOutTable(player *game.Player, msg *api.ReqKickOutTable) (proto
if player.GameTable.Stage != 1 { if player.GameTable.Stage != 1 {
return &api.ResFail{Msg: "牌局进行中不能踢人"}, nil return &api.ResFail{Msg: "牌局进行中不能踢人"}, nil
} }
for i, p := range player.GameTable.Players {
if p != nil && p.Id == msg.PlayerId { found, p := player.GameTable.RemovePlayer(msg.PlayerId)
// 从牌桌移除该玩家 if found {
player.GameTable.Players[i] = nil // 结算玩家金额
// 被对踢出人发送消息 store.NetAccounts.Get(p.Id).SettlePlayerChip()
account := store.NetAccounts.Get(p.Id) // 被踢玩家消息
account.Player = nil // 解除账户绑定 player.Client.Write(&api.ResKickOutTable{})
account.IncrementBalance(player.Chip) // 筹码返还账户
p.ProtoWriter.Write(&api.ResKickOutTable{}) // 被踢玩家消息
break
}
} }
// 通知牌桌所有玩家 // 通知牌桌所有玩家
player.GameTable.NoticeGameFullStatus() player.GameTable.NoticeGameFullStatus()
@ -223,7 +226,7 @@ func HandleReqKickOutTable(player *game.Player, msg *api.ReqKickOutTable) (proto
} }
// HandleReqLeaveTable 离开桌面 // 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() player.GameTable.PlayersLock.Lock()
defer player.GameTable.PlayersLock.Unlock() defer player.GameTable.PlayersLock.Unlock()
@ -237,16 +240,10 @@ func HandleReqLeaveTable(player *game.Player, msg *api.ReqLeaveTable) (proto.Mes
return &api.ResFail{Msg: "房主不能退出,请解散房间"}, nil return &api.ResFail{Msg: "房主不能退出,请解散房间"}, nil
} }
// 从牌桌移除 // 从牌桌移除
for i, p := range player.GameTable.Players { found, p := player.GameTable.RemovePlayer(player.Id)
if p.Id == player.Id { if found {
player.GameTable.Players[i] = nil store.NetAccounts.Get(p.Id).SettlePlayerChip()
break
} }
}
account := store.NetAccounts.Get(player.Id)
account.Player = nil // 解除账户绑定
account.IncrementBalance(player.Chip) // 筹码返还账户
// 通知牌桌其他玩家 // 通知牌桌其他玩家
player.GameTable.NoticeGameFullStatus() player.GameTable.NoticeGameFullStatus()
@ -254,7 +251,7 @@ func HandleReqLeaveTable(player *game.Player, msg *api.ReqLeaveTable) (proto.Mes
} }
// HandleReqGameFullStatus 获取牌桌当前所有状态 // 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 { if player.GameTable == nil {
return &api.ResFail{Msg: "当前未加入牌桌"}, nil return &api.ResFail{Msg: "当前未加入牌桌"}, nil
} }
@ -274,7 +271,7 @@ func HandleReqGameFullStatus(player *game.Player, msg *api.ReqGameFullStatus) (p
} }
// HandleReqReadyStart 准备开始游戏 // 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() player.Lock.Lock()
defer player.Lock.Unlock() defer player.Lock.Unlock()
if player.Id == player.GameTable.MasterId { if player.Id == player.GameTable.MasterId {
@ -297,9 +294,11 @@ func HandleReqReadyStart(player *game.Player, msg *api.ReqReadyStart) (proto.Mes
return &api.ResSuccess{}, nil return &api.ResSuccess{}, nil
} }
func HandleReqCancelReady(player *game.Player, msg *api.ReqCancelReady) (proto.Message, error) { func HandleReqCancelReady(player *game.Player, _ *api.ReqCancelReady) (proto.Message, error) {
player.Lock.Lock() // 和 GameStart 互斥
defer player.Lock.Unlock() player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock()
if player.Id == player.GameTable.MasterId { if player.Id == player.GameTable.MasterId {
return &api.ResFail{Msg: "房主不用准备"}, nil return &api.ResFail{Msg: "房主不用准备"}, nil
} }
@ -312,7 +311,7 @@ func HandleReqCancelReady(player *game.Player, msg *api.ReqCancelReady) (proto.M
} }
// HandleReqDismissGameTable 解散牌桌 // 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() player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock() defer player.GameTable.Lock.Unlock()
if !collect.In(player.GameTable.Stage, 1, 7) { if !collect.In(player.GameTable.Stage, 1, 7) {
@ -330,23 +329,16 @@ func HandleReqDismissGameTable(player *game.Player, msg *api.ReqDismissGameTable
account.Player = nil account.Player = nil
player.GameTable.Players[i] = nil player.GameTable.Players[i] = nil
if p.Id != player.Id { if p.Id != player.Id {
p.ProtoWriter.Write(&api.ResDismissGameTable{}) p.Client.Write(&api.ResDismissGameTable{})
} }
} }
return &api.ResDismissGameTable{}, nil return &api.ResDismissGameTable{}, nil
} }
// HandleReqGameStart 游戏开始,初始化,扣除大小盲注,每个玩家发2张牌 // 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() player.GameTable.Lock.Lock()
defer player.GameTable.Lock.Unlock() defer player.GameTable.Lock.Unlock()
// 锁定牌桌所有玩家
for _, p := range player.GameTable.Players {
if p != nil {
p.Lock.Lock()
defer p.Lock.Unlock()
}
}
table := player.GameTable table := player.GameTable
if player.Id != table.MasterId { 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) { if !collect.In(table.Stage, 1, 7) {
return &api.ResFail{Msg: fmt.Sprintf("牌局状态有误%d", table.Stage)}, nil return &api.ResFail{Msg: fmt.Sprintf("牌局状态有误%d", table.Stage)}, nil
} }
if table.PlayerCount() < 2 {
return &api.ResFail{Msg: "至少需两位玩家可开始"}, nil
}
// 检查所有玩家准备状态 // 检查所有玩家准备状态
for _, p := range table.Players { for _, p := range table.Players {
if p != nil && p.Id != table.MasterId && p.Status != 2 { 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 下注 // HandleReqBetting 下注
func HandleReqBetting(player *game.Player, msg *api.ReqBetting) (proto.Message, error) { func HandleReqBetting(player *game.Player, msg *api.ReqBetting) (proto.Message, error) {
player.Lock.Lock()
defer player.Lock.Unlock()
if player.Status != 6 { if player.Status != 6 {
return &api.ResFail{Msg: "当前未轮到您下注"}, nil return &api.ResFail{Msg: "当前未轮到您下注"}, nil
} }

7
internal/session/net_account.go

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