xy há 2 semanas atrás
commit
5ce09034fb
61 ficheiros alterados com 5347 adições e 0 exclusões
  1. BIN
      .DS_Store
  2. BIN
      bin/.DS_Store
  3. 216 0
      bin/client_msg/common.proto
  4. 9 0
      bin/client_msg/msg.ts
  5. 6 0
      bin/conf/server.json
  6. 17 0
      bin/gamedata/robot_head_image.json
  7. 17 0
      bin/gamedata/robot_name.json
  8. 48 0
      bin/gamedata/teen_patti_conf.json
  9. 2 0
      bin/log/.gitignore
  10. 22 0
      gen_proto.sh
  11. BIN
      src/server/.DS_Store
  12. 18 0
      src/server/base/skeleton.go
  13. 12 0
      src/server/common/user_common.go
  14. 31 0
      src/server/conf/conf.go
  15. 31 0
      src/server/conf/json.go
  16. 87 0
      src/server/datacenter/usercenterd.go
  17. 143 0
      src/server/db/mongodb/mongodbmgr.go
  18. 49 0
      src/server/db/mongodb/userMongodb.go
  19. 135 0
      src/server/db/mysql/mysqlmgr.go
  20. 159 0
      src/server/db/redis/redismgr.go
  21. 160 0
      src/server/db/redis/userRedis.go
  22. 17 0
      src/server/events/events.go
  23. BIN
      src/server/game/.DS_Store
  24. 10 0
      src/server/game/external.go
  25. 121 0
      src/server/game/internal/chanrpc.go
  26. 38 0
      src/server/game/internal/handler.go
  27. 57 0
      src/server/game/internal/module.go
  28. 192 0
      src/server/game/ludo/battle.go
  29. 36 0
      src/server/game/ludo/battle_manager.go
  30. 75 0
      src/server/game/ludo/color_recv.go
  31. 73 0
      src/server/game/ludo/color_send.go
  32. 30 0
      src/server/game/ludo/event.go
  33. 109 0
      src/server/game/ludo/ludo.go
  34. 269 0
      src/server/game/ludo/ludo_room_data.go
  35. 43 0
      src/server/game/ludo/match.go
  36. 191 0
      src/server/game/ludo/room.go
  37. 21 0
      src/server/gamedata/reader.go
  38. 9 0
      src/server/gate/external.go
  39. 29 0
      src/server/gate/internal/module.go
  40. 14 0
      src/server/gate/router.go
  41. 19 0
      src/server/go.mod
  42. 30 0
      src/server/go.sum
  43. 10 0
      src/server/hall/external.go
  44. 67 0
      src/server/hall/internal/handler.go
  45. 23 0
      src/server/hall/internal/module.go
  46. 10 0
      src/server/login/external.go
  47. 74 0
      src/server/login/internal/handler.go
  48. 23 0
      src/server/login/internal/module.go
  49. 29 0
      src/server/main.go
  50. 1 0
      src/server/models/game.model.go
  51. 56 0
      src/server/models/user.model.go
  52. 2208 0
      src/server/msg/common.pb.go
  53. 59 0
      src/server/msg/msg.go
  54. 16 0
      src/server/msg/test.proto
  55. BIN
      src/server/server
  56. 81 0
      src/server/tools/time.go
  57. 10 0
      src/server/user/external.go
  58. 13 0
      src/server/user/internal/handler.go
  59. 23 0
      src/server/user/internal/module.go
  60. 82 0
      src/server/user/user.go
  61. 17 0
      src/server/user/userData.go

BIN
.DS_Store


BIN
bin/.DS_Store


+ 216 - 0
bin/client_msg/common.proto

@@ -0,0 +1,216 @@
+syntax = "proto3";
+
+option go_package = "./msg";
+
+enum roleType {
+    ROLE_TYPE_UNKNOWN = 0;
+    RED        = 1;  // 无操作
+    BLUE   = 4;
+    YELLOW   = 3;
+    GREEN   = 2;
+}
+
+enum OptType {
+    OPT_TYPE_UNKNOWN = 0;
+    ZHI_SHAI_ZI     = 1;  
+    SELECT_ROLE   = 2;
+}
+
+enum roomType {
+  ROOM_TYPE_UNKNOWN = 0;
+  SHUANG_REN = 1;
+  SIREN_REN = 2;
+}
+
+enum roomMode {
+  ROOM_MODE_UNKNOWN = 0;
+  REN_JI = 1;
+  WAN_JIA = 2;
+}
+
+enum roadType {
+  ROAD_TYPE_UNKNOWN = 0;
+  HOME =1;
+}
+
+enum playerStatus {
+  PLAYER_STATUS_UNKNOWN = 0;
+  SZ_ANIMATION =1;
+  COLOR_FINISH = 2;
+  COLOR_KICK = 3;
+}
+
+enum roomStatus {
+  AWAIT = 0;
+  START = 1;
+  END = 2;
+}
+
+message round {
+  roleType m_color = 1; 
+  string m_road = 2;
+  OptType opt = 3;
+  int32 szNumber = 4;
+}
+
+message RoomInfo {
+  repeated RoleData roles = 1;
+  repeated ColorData colors = 2;
+  roomType room_type = 3;
+  roomMode room_mode = 4;
+  roleType cur_round_color = 5;
+  repeated round rounds = 6;
+  repeated ColorData finish_colors = 7;
+  repeated ColorData kict_colors = 8;
+  int32 id = 9;
+  int32 room_level = 10; //房间的每个等级都对应着不同的奖励和消耗
+  roomStatus room_status = 11;
+}
+
+message RoleData {
+  roleType m_color = 1;
+  int32 m_seat = 2;
+  string m_id = 3;
+  roadType m_cur_road = 4;
+  int32 step = 5;
+  int32 old_setp = 6;
+}
+
+message ColorData {
+  string m_id = 1;
+  roleType m_color = 2;
+  bool is_kick = 3;
+  bool is_finish = 4;
+  int32 time_out_num = 5;
+  string m_name = 6;
+  string m_head = 7;
+  int32 rank_num = 8;
+  int32 m_coin = 9;
+}
+
+message MoveStepData {
+  string m_id = 1;
+  int32 step = 2;
+  int32 old_setp = 3;
+}
+
+message SendColorSz {
+  roleType color = 1;  // 
+}
+
+message SendRoleMove {
+  roleType color = 1;  // 
+  string roleId = 2;
+}
+
+message NotifyPlayerSzNumber {
+  roleType color = 1;  // 
+  int32 szNumber = 2;   // 
+  MsgError err_msg = 3;
+}
+
+message NotifyPlayerMove {
+  roleType color = 1;  // 
+  MoveStepData step = 2;   // 
+  repeated RoleData kick = 3;
+}
+
+message NotifyPlayerOpt {
+  roleType color = 1;  // 
+  OptType opt = 2;   // 
+  repeated RoleData canMoveRoles = 3;
+}
+
+message NotifyPlayerStatus {
+  roleType color = 1;  // 
+  playerStatus status = 2;
+  repeated ColorData colors = 3;
+}
+
+
+message NotifySettlement {
+  roleType color = 1;  // 
+  repeated ColorData finish_colors = 2;
+}
+
+//----------------------------------
+
+
+//用户信息
+message UserInfo {
+  string UserId = 1;
+  string m_head = 2;
+  int32 m_coin = 3;
+  string name = 4;
+  int32 room_id = 5; //如果用户在某一个房间玩,他的id就一直存在,
+  
+}
+//登录 请求
+message ReqLogin {
+  string account = 1;
+  string password = 2;
+}
+
+//登录 响应
+message ResLogin {
+  string userId = 1;
+  string nikeName = 2;
+  UserInfo userInfo = 3;
+  MsgError err_msg = 4;
+}
+//注册 请求
+message ReqRegister {
+  string nikeName = 1;
+  string account = 2;
+  string password = 3;
+  string m_head = 4;
+}
+//注册 响应
+message ResRegister {
+  bool success = 1;
+  MsgError err_msg = 2;
+}
+
+
+
+//进入大厅
+message EnterHall {
+  string userId = 1;
+}
+//进入大厅响应
+message ResEnterHall {
+  bool success = 1;
+  MsgError err_msg = 2;
+}
+
+//离开大厅
+message LeaveHall {
+  string userId = 1;
+}
+
+//匹配ludo
+message MatchLudo {
+  roomType select_room_type = 1;
+  int32 room_level = 2;
+  roleType select_color = 3;
+}
+//匹配ludo 响应
+message ResMatchLudo {
+  bool success = 1;
+  MsgError err_msg = 2;
+  RoomInfo room = 3;
+}
+//error
+message MsgError {
+  int32 error_code = 1;
+  string error_msg = 2;
+}
+
+
+message ResHeartBeat {
+  string msg = 1;
+}
+
+message ReqHeartBeat {
+  string msg = 1;
+}

+ 9 - 0
bin/client_msg/msg.ts

@@ -0,0 +1,9 @@
+// 自动生成的消息ID映射
+export enum MsgID {
+    ReqLogin = 0,
+    ResLogin = 1,
+    EnterHall = 2,
+    LeaveHall = 3,
+    MatchLudo = 4,
+    ResHeartBeat = 5,
+}

+ 6 - 0
bin/conf/server.json

@@ -0,0 +1,6 @@
+{
+	"LogLevel": "debug",
+	"LogPath": "/Users/xy/Desktop/ludo_server/bin/log",
+	"WSAddr": "0.0.0.0:3653",
+	"MaxConnNum": 20000
+}

+ 17 - 0
bin/gamedata/robot_head_image.json

@@ -0,0 +1,17 @@
+{
+    "head_image": [
+        "img_head_1",
+        "img_head_2",
+        "img_head_3",
+        "img_head_4",
+        "img_head_5",
+        "img_head_6",
+        "img_head_7",
+        "img_head_8",
+        "img_head_9",
+        "img_head_10",
+        "img_head_11",
+        "img_head_12",
+        "img_head_13"
+    ]
+}

+ 17 - 0
bin/gamedata/robot_name.json

@@ -0,0 +1,17 @@
+{
+    "name": [
+        "阿三",
+        "阿四",
+        "阿五",
+        "阿六",
+        "阿七",
+        "阿八",
+        "阿九",
+        "阿十",
+        "阿十一",
+        "阿十二",
+        "阿十三",
+        "阿十四",
+        "阿十五"
+    ]
+}

+ 48 - 0
bin/gamedata/teen_patti_conf.json

@@ -0,0 +1,48 @@
+{
+    "game_id": "teen_patti",
+    "name": "teen_patti",
+    "status": 1,
+    "think_time": 10,
+    "room_list": [
+        {
+            "type":"cash",
+            "boot": "1",
+            "minBuyin": "10",
+            "chaalLimmit": "128",
+            "potLimit": "1024",
+            "totalPlayers":"1000",
+            "roomLevel": "low",
+            "confId": "1"
+        },
+        {
+            "type":"cash",
+            "boot": "10",
+            "minBuyin": "1000",
+            "chaalLimmit": "12800",
+            "potLimit": "10240",
+            "totalPlayers":"1000",
+            "roomLevel": "mid",
+            "confId": "2"
+        },
+        {
+            "type":"cash",
+            "boot": "100",
+            "minBuyin": "10000",
+            "chaalLimmit": "128000",
+            "potLimit": "102400",
+            "totalPlayers":"1000",
+            "roomLevel": "high",
+            "confId": "3"
+        },
+        {
+            "type":"practice",
+            "boot": "100",
+            "minBuyin": "10000",
+            "chaalLimmit": "128000",
+            "potLimit": "102400",
+            "totalPlayers":"1000",
+            "roomLevel": "practice",
+            "confId": "4"
+        }
+    ]
+}

+ 2 - 0
bin/log/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 22 - 0
gen_proto.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# 设置 GOPATH
+export GOPATH=/home/zjh/teen_patti
+
+# 添加 GOPATH/bin 到 PATH
+export PATH=$PATH:$GOPATH/bin:/usr/local/bin:$(go env GOPATH)/bin
+
+# 进入 proto 文件目录
+cd bin/client_msg
+
+# 生成 proto 文件
+protoc --go_out=. common.proto
+
+# 创建目标目录(如果不存在)
+mkdir -p ../../src/server/msg
+
+# 移动生成的文件到目标目录
+mv msg/common.pb.go ../../src/server/msg/
+rm -rf msg
+
+echo "Proto files generated and moved successfully!" 

BIN
src/server/.DS_Store


+ 18 - 0
src/server/base/skeleton.go

@@ -0,0 +1,18 @@
+package base
+
+import (
+	"github.com/name5566/leaf/chanrpc"
+	"github.com/name5566/leaf/module"
+	"server/conf"
+)
+
+func NewSkeleton() *module.Skeleton {
+	skeleton := &module.Skeleton{
+		GoLen:              conf.GoLen,
+		TimerDispatcherLen: conf.TimerDispatcherLen,
+		AsynCallLen:        conf.AsynCallLen,
+		ChanRPCServer:      chanrpc.NewServer(conf.ChanRPCLen),
+	}
+	skeleton.Init()
+	return skeleton
+}

+ 12 - 0
src/server/common/user_common.go

@@ -0,0 +1,12 @@
+package common
+
+type UserData struct {
+	Id     string
+	Status int
+	GameId string
+	// Teen_Patti_Room *room.Room
+	Head_Image string
+	Nickname   string
+	Room_id    string
+	Points     int32
+}

+ 31 - 0
src/server/conf/conf.go

@@ -0,0 +1,31 @@
+package conf
+
+import (
+	"log"
+	"time"
+)
+
+var (
+	// log conf
+	LogFlag = log.LstdFlags
+
+	// gate conf
+	PendingWriteNum        = 2000
+	MaxMsgLen       uint32 = 4096
+	HTTPTimeout            = 10 * time.Second
+	LenMsgLen              = 2
+	LittleEndian           = false
+
+	// skeleton conf
+	GoLen              = 10000
+	TimerDispatcherLen = 10000
+	AsynCallLen        = 10000
+	ChanRPCLen         = 10000
+)
+
+var (
+	MongoDBAddr = "mongodb://localhost:27017"
+	MongoDBName = "teen_patti"
+	RedisDBAddr = "localhost:6379"
+	MysqlDBAddr = "root:password@tcp(localhost:3306)/teen_patti?charset=utf8mb4&parseTime=True"
+)

+ 31 - 0
src/server/conf/json.go

@@ -0,0 +1,31 @@
+package conf
+
+import (
+	"encoding/json"
+	"os"
+
+	"github.com/name5566/leaf/log"
+)
+
+var Server struct {
+	LogLevel    string
+	LogPath     string
+	WSAddr      string
+	CertFile    string
+	KeyFile     string
+	TCPAddr     string
+	MaxConnNum  int
+	ConsolePort int
+	ProfilePath string
+}
+
+func init() {
+	data, err := os.ReadFile("../../bin/conf/server.json")
+	if err != nil {
+		log.Fatal("%v", err)
+	}
+	err = json.Unmarshal(data, &Server)
+	if err != nil {
+		log.Fatal("%v", err)
+	}
+}

+ 87 - 0
src/server/datacenter/usercenterd.go

@@ -0,0 +1,87 @@
+package usercenter
+
+import (
+	"log"
+	"server/models"
+	"sync"
+	"time"
+)
+
+type UserCenter struct {
+	activeCache sync.Map // 当前正在使用的主缓存
+	backupCache sync.Map // 刷新期间使用的备用缓存
+	mu          sync.Mutex
+}
+
+func NewUserCenter() *UserCenter {
+	return &UserCenter{}
+}
+
+// LoadThreeDayUsers 分页加载最近三天登录的用户到备用缓存
+func (uc *UserCenter) LoadThreeDayUsers() error {
+	const batchSize = 1000 // 每次加载1000条记录
+	skip := 0
+
+	// 清空备用缓存
+	uc.backupCache = sync.Map{}
+
+	for {
+		users, err := models.GetThreeDayUsersWithPagination(skip, batchSize)
+		if err != nil {
+			return err
+		}
+
+		// 如果没有更多用户,跳出循环
+		if len(users) == 0 {
+			break
+		}
+
+		// 将用户数据存入备用缓存
+		for _, user := range users {
+			uc.backupCache.Store(user.UserID, user)
+		}
+
+		skip += batchSize
+	}
+
+	log.Println("User data loaded into backup cache")
+	return nil
+}
+
+// SwitchCache 切换主缓存与备用缓存
+func (uc *UserCenter) SwitchCache() {
+	uc.mu.Lock()
+	defer uc.mu.Unlock()
+
+	// 用备用缓存替换主缓存
+	uc.activeCache, uc.backupCache = uc.backupCache, sync.Map{}
+	log.Println("Cache switched successfully")
+}
+
+// GetUserFromCache 从主缓存中获取用户
+func (uc *UserCenter) GetUserFromCache(userID int64) (*models.User, bool) {
+	value, ok := uc.activeCache.Load(userID)
+	if !ok {
+		return nil, false
+	}
+	user, ok := value.(models.User)
+	return &user, ok
+}
+
+// StartAutoRefresh 定期刷新备用缓存并切换
+func (uc *UserCenter) StartAutoRefresh(interval time.Duration) {
+	go func() {
+		ticker := time.NewTicker(interval)
+		defer ticker.Stop()
+
+		for range ticker.C {
+			log.Println("Starting to refresh user cache...")
+			err := uc.LoadThreeDayUsers()
+			if err != nil {
+				log.Printf("Failed to refresh user cache: %v", err)
+				continue
+			}
+			uc.SwitchCache() // 切换缓存
+		}
+	}()
+}

+ 143 - 0
src/server/db/mongodb/mongodbmgr.go

@@ -0,0 +1,143 @@
+package mongodbmgr
+
+import (
+	"server/conf"
+
+	"github.com/name5566/leaf/db/mongodb"
+	"github.com/name5566/leaf/log"
+
+	// "gopkg.in/mgo.v2"
+	"gopkg.in/mgo.v2/bson"
+)
+
+// 连接消息
+var dialContext = new(mongodb.DialContext)
+
+func init() {
+	Connect()
+	// test()
+}
+func Connect() {
+	c, err := mongodb.Dial(conf.MongoDBAddr, 10)
+	if err != nil {
+		log.Error(err.Error())
+		return
+	}
+	//defer c.Close()
+	// index
+	c.EnsureUniqueIndex(conf.MongoDBName, "login", []string{"user_id"})
+	log.Release("mongodb Connect success")
+	dialContext = c
+}
+
+// Collection 定义集合操作接口
+type Collection struct {
+	DB         string
+	Collection string
+}
+
+// NewCollection 创建集合操作实例
+func NewCollection(db, collection string) *Collection {
+	return &Collection{
+		DB:         db,
+		Collection: collection,
+	}
+}
+
+// Insert 插入文档
+func (c *Collection) Insert(doc interface{}) error {
+	s := dialContext.Ref()
+	defer dialContext.UnRef(s)
+
+	return s.DB(c.DB).C(c.Collection).Insert(doc)
+}
+
+// Delete 删除文档
+func (c *Collection) Delete(query bson.M) error {
+	s := dialContext.Ref()
+	defer dialContext.UnRef(s)
+
+	return s.DB(c.DB).C(c.Collection).Remove(query)
+}
+
+// Update 更新文档
+func (c *Collection) Update(query bson.M, update bson.M) error {
+	s := dialContext.Ref()
+	defer dialContext.UnRef(s)
+
+	return s.DB(c.DB).C(c.Collection).Update(query, bson.M{"$set": update})
+}
+
+// FindOne 查询单个文档
+func (c *Collection) FindOne(query bson.M, result interface{}) error {
+	s := dialContext.Ref()
+	defer dialContext.UnRef(s)
+
+	return s.DB(c.DB).C(c.Collection).Find(query).One(result)
+}
+
+// FindMany 查询多个文档
+func (c *Collection) FindMany(query bson.M, skip, limit int, result interface{}) error {
+	s := dialContext.Ref()
+	defer dialContext.UnRef(s)
+
+	return s.DB(c.DB).C(c.Collection).Find(query).Skip(skip).Limit(limit).All(result)
+}
+
+// func test() {
+// 	// 1. 用户集合操作
+// 	type User struct {
+// 		UserID   int64  `bson:"user_id"`
+// 		Name     string `bson:"name"`
+// 		Password string `bson:"password"`
+// 	}
+
+// 	userColl := NewCollection(conf.MongoDBName, "users")
+
+// 	// 插入用户
+// 	user := &User{UserID: 1001, Name: "player1"}
+// 	err := userColl.Insert(user)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+
+// 	// 查询用户
+// 	findUser := new(User)
+// 	err = userColl.FindOne(bson.M{"user_id": 1001}, findUser)
+
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+// 	// 2. 游戏记录集合操作
+// 	type GameRecord struct {
+// 		GameID    int64   `bson:"game_id"`
+// 		Players   []int64 `bson:"players"`
+// 		CreatedAt int64   `bson:"created_at"`
+// 	}
+
+// 	gameColl := NewCollection("teen_patti", "game_records")
+
+// 	// 插入游戏记录
+// 	record := &GameRecord{
+// 		GameID:    1,
+// 		Players:   []int64{1001, 1002},
+// 		CreatedAt: time.Now().Unix(),
+// 	}
+// 	err = gameColl.Insert(record)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+
+// 	// 查询游戏记录列表
+// 	var records []GameRecord
+// 	err = gameColl.FindMany(
+// 		bson.M{"players": 1001},
+// 		0,
+// 		10,
+// 		&records,
+// 	)
+// 	if err != nil {
+// 		log.Error(err.Error())
+// 	}
+// 	log.Error("records:", records)
+// }

+ 49 - 0
src/server/db/mongodb/userMongodb.go

@@ -0,0 +1,49 @@
+package mongodbmgr
+
+import (
+	"log"
+	"server/common"
+	"server/conf"
+	"server/msg"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+// 定义用户集合操作
+var userCollection = NewCollection(conf.MongoDBName, "users")
+
+// 新增用户到 MongoDB
+func AddUser(userData *msg.UserInfo) error {
+	// 创建插入的用户数据
+	err := userCollection.Insert(userData)
+	if err != nil {
+		log.Printf("Error inserting user data into MongoDB: %v", err)
+		return err
+	}
+	log.Printf("User data added to MongoDB for userId: %s", userData.UserId)
+	return nil
+}
+
+// 更新用户信息到 MongoDB
+func UpdateUserInfo(userData *msg.UserInfo) error {
+	// 使用用户 ID 来更新用户数据
+	err := userCollection.Update(bson.M{"id": userData.UserId}, bson.M{"$set": userData})
+	if err != nil {
+		log.Printf("Error updating user data in MongoDB: %v", err)
+		return err
+	}
+	log.Printf("User data updated in MongoDB for userId: %s", userData.UserId)
+	return nil
+}
+
+// 获取用户信息从 MongoDB
+func GetUserInfoFromMongoDB(userId string) (*common.UserData, error) {
+	var userData common.UserData
+	err := userCollection.FindOne(bson.M{"id": userId}, &userData) // 查找用户ID
+	if err != nil {
+		log.Printf("Error fetching user data from MongoDB: %v", err)
+		// 如果没有找到用户数据,可以返回 nil 或者处理错误
+		return nil, err
+	}
+	return &userData, nil
+}

+ 135 - 0
src/server/db/mysql/mysqlmgr.go

@@ -0,0 +1,135 @@
+package mysqlmgr
+
+import (
+	"database/sql"
+	"server/conf"
+	"time"
+
+	"github.com/name5566/leaf/log"
+
+	_ "github.com/go-sql-driver/mysql"
+)
+
+var (
+	db *sql.DB
+)
+
+func init() {
+	Connect()
+}
+
+// Connect 初始化数据库连接
+func Connect() {
+	var err error
+	db, err = sql.Open("mysql", conf.MysqlDBAddr)
+	if err != nil {
+		panic(err)
+	}
+
+	// 设置连接池
+	db.SetMaxIdleConns(10)           // 最大空闲连接数
+	db.SetMaxOpenConns(100)          // 最大打开连接数
+	db.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
+}
+
+// Insert 插入数据
+func Insert(query string, args ...interface{}) (int64, error) {
+	result, err := db.Exec(query, args...)
+	if err != nil {
+		return 0, err
+	}
+	return result.LastInsertId()
+}
+
+// Update 更新数据
+func Update(query string, args ...interface{}) (int64, error) {
+	result, err := db.Exec(query, args...)
+	if err != nil {
+		return 0, err
+	}
+	return result.RowsAffected()
+}
+
+// Delete 删除数据
+func Delete(query string, args ...interface{}) (int64, error) {
+	result, err := db.Exec(query, args...)
+	if err != nil {
+		return 0, err
+	}
+	return result.RowsAffected()
+}
+
+// QueryRow 查询单行
+func QueryRow(query string, args ...interface{}) *sql.Row {
+	return db.QueryRow(query, args...)
+}
+
+// Query 查询多行
+func Query(query string, args ...interface{}) (*sql.Rows, error) {
+	return db.Query(query, args...)
+}
+
+// 测试函数
+func test() {
+	// 创建用户表
+	createTable := `
+	CREATE TABLE IF NOT EXISTS users (
+		id INT AUTO_INCREMENT,
+		name VARCHAR(255) NOT NULL,
+		balance DECIMAL(10,2) DEFAULT 0,
+		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+		PRIMARY KEY (id)
+	) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+	`
+	_, err := db.Exec(createTable)
+	if err != nil {
+		log.Debug("Create table error:", err)
+		return
+	}
+
+	// 插入数据
+	id, err := Insert("INSERT INTO users (name, balance) VALUES (?, ?)", "player1", 1000.00)
+	if err != nil {
+		log.Debug("Insert error:", err)
+		return
+	}
+	log.Debug("Inserted user ID: %d\n", id)
+
+	// 更新数据
+	affected, err := Update("UPDATE users SET balance = balance + ? WHERE id = ?", 100.00, id)
+	if err != nil {
+		log.Debug("Update error:", err)
+		return
+	}
+	log.Debug("Updated %d rows\n", affected)
+
+	// 查询单行
+	var name string
+	var balance float64
+	err = QueryRow("SELECT name, balance FROM users WHERE id = ?", id).Scan(&name, &balance)
+	if err != nil {
+		log.Debug("Query error:", err)
+		return
+	}
+	log.Debug("User: %s, Balance: %.2f\n", name, balance)
+
+	// 查询多行
+	rows, err := Query("SELECT id, name, balance FROM users")
+	if err != nil {
+		log.Debug("Query error:", err)
+		return
+	}
+	defer rows.Close()
+
+	for rows.Next() {
+		var id int
+		var name string
+		var balance float64
+		err := rows.Scan(&id, &name, &balance)
+		if err != nil {
+			log.Debug("Scan error:", err)
+			continue
+		}
+		log.Debug("ID: %d, Name: %s, Balance: %.2f\n", id, name, balance)
+	}
+}

+ 159 - 0
src/server/db/redis/redismgr.go

@@ -0,0 +1,159 @@
+package redismgr
+
+import (
+	"server/conf"
+	"time"
+
+	"github.com/gomodule/redigo/redis"
+	"github.com/name5566/leaf/log"
+)
+
+var (
+	pool *redis.Pool
+)
+
+func init() {
+	Connect()
+}
+
+// Connect 初始化连接池
+func Connect() {
+	pool = &redis.Pool{
+		MaxIdle:     256,              // 最大空闲连接数
+		MaxActive:   0,                // 最大连接数,0表示不限制
+		IdleTimeout: time.Second * 60, // 空闲超时时间
+		Dial: func() (redis.Conn, error) {
+			return redis.Dial(
+				"tcp",
+				conf.RedisDBAddr,
+				redis.DialPassword(""), // 如果有密码
+				redis.DialDatabase(0),  // 选择数据库
+			)
+		},
+	}
+	log.Release("redis Connect success")
+}
+
+// GetPool 获取连接池
+func GetPool() *redis.Pool {
+	return pool
+}
+
+// Set 设置键值对
+func Set(key string, value interface{}, expiration ...int) error {
+	conn := pool.Get()
+	defer conn.Close()
+
+	if len(expiration) > 0 {
+		_, err := conn.Do("SETEX", key, expiration[0], value)
+		return err
+	}
+	_, err := conn.Do("SET", key, value)
+	return err
+}
+
+// Get 获取值
+func Get(key string) (string, error) {
+	conn := pool.Get()
+	defer conn.Close()
+
+	reply, err := redis.String(conn.Do("GET", key))
+	if err == redis.ErrNil {
+		return "", nil
+	}
+	return reply, err
+}
+
+// Delete 删除键
+func Delete(key string) error {
+	conn := pool.Get()
+	defer conn.Close()
+
+	_, err := conn.Do("DEL", key)
+	return err
+}
+
+// Exists 检查键是否存在
+func Exists(key string) (bool, error) {
+	conn := pool.Get()
+	defer conn.Close()
+
+	return redis.Bool(conn.Do("EXISTS", key))
+}
+
+// HashSet 设置哈希表字段
+func HashSet(key, field string, value interface{}) error {
+	conn := pool.Get()
+	defer conn.Close()
+
+	_, err := conn.Do("HSET", key, field, value)
+	return err
+}
+
+// HashGet 获取哈希表字段
+func HashGet(key, field string) (string, error) {
+	conn := pool.Get()
+	defer conn.Close()
+
+	reply, err := redis.String(conn.Do("HGET", key, field))
+	if err == redis.ErrNil {
+		return "", nil
+	}
+	return reply, err
+}
+
+// ListPush 将值推入列表
+func ListPush(key string, value interface{}) error {
+	conn := pool.Get()
+	defer conn.Close()
+
+	_, err := conn.Do("LPUSH", key, value)
+	return err
+}
+
+// ListRange 获取列表范围内的元素
+func ListRange(key string, start, stop int) ([]string, error) {
+	conn := pool.Get()
+	defer conn.Close()
+
+	return redis.Strings(conn.Do("LRANGE", key, start, stop))
+}
+
+// 测试函数
+func test() {
+	// 测试字符串操作
+	err := Set("test_key", "test_value", 60)
+	if err != nil {
+		log.Debug("Set error:", err)
+	}
+
+	value, err := Get("test_key")
+	if err != nil {
+		log.Debug("Get error:", err)
+	}
+	log.Debug("Get value:", value)
+
+	// 测试哈希表操作
+	err = HashSet("user:1001", "name", "player1")
+	if err != nil {
+		log.Debug("HashSet error:", err)
+	}
+
+	name, err := HashGet("user:1001", "name")
+	if err != nil {
+		log.Debug("HashGet error:", err)
+	}
+	log.Debug("HashGet name:", name)
+
+	// 测试列表操作
+	err = ListPush("game:list", "game1")
+	if err != nil {
+		log.Debug("ListPush error:", err)
+	}
+
+	games, err := ListRange("game:list", 0, -1)
+	if err != nil {
+		log.Debug("ListRange error:", err)
+	}
+	log.Debug("Games:", games)
+}

+ 160 - 0
src/server/db/redis/userRedis.go

@@ -0,0 +1,160 @@
+package redismgr
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"server/msg"
+	"strconv"
+
+	"encoding/base64"
+
+	"github.com/gomodule/redigo/redis"
+	"github.com/name5566/leaf/log"
+)
+
+// 保存用户信息到 Redis 哈希表
+func SaveUserInfoToRedis(userData *msg.UserInfo) error {
+	// 将用户数据转换为 JSON 字符串
+	userDataJson, err := json.Marshal(userData)
+	if err != nil {
+		log.Debug("Error marshalling user data: %v", err)
+		return err
+	}
+
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 使用 HSET 存储用户数据
+	_, err = conn.Do("HSET", "user:"+userData.UserId, "data", string(userDataJson))
+	if err != nil {
+		log.Debug("Error saving user data to Redis: %v", err)
+		return err
+	}
+
+	// 设置过期时间为3天
+	_, err = conn.Do("EXPIRE", "user:"+userData.UserId, 86400*3) // 86400*3 = 3天
+	if err != nil {
+		log.Debug("Error setting expiry for user data in Redis: %v", err)
+		return err
+	}
+	// 进入redis队列存mongodb .
+	// UpdateUserInfo(userData)
+	log.Debug("User data saved to Redis for userId: %s", userData.UserId)
+	return nil
+}
+
+// IncrementUserTotal 将UserTotal的值原子性加1,返回增加后的新值
+func IncrementUserTotal() (int64, error) {
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 使用INCR命令原子性增加
+	newVal, err := redis.Int64(conn.Do("INCR", "UserTotal"))
+	if err != nil {
+		log.Debug("Failed to increment UserTotal: %v", err)
+		return 0, fmt.Errorf("redis INCR failed: %w", err)
+	}
+
+	return newVal, nil
+}
+
+func GetUserTotal() int64 {
+	conn := GetPool().Get()
+	defer conn.Close()
+	//获取key为UserTotal的 int 类型
+
+	// 使用GET命令获取key为"UserTotal"的值
+	reply, err := redis.Int64(conn.Do("GET", "UserTotal"))
+	if err != nil {
+		if err == redis.ErrNil {
+			log.Debug("Key 'UserTotal' does not exist in Redis")
+			return 0
+		}
+		log.Debug("Failed to get 'UserTotal' from Redis: %v", err)
+		return 0
+	}
+	return reply
+}
+
+// 根据账号密码设置用户id
+func SetUserIDFromRedisByAP(account string, password string) (string, bool) {
+	newUserId, err := IncrementUserTotal()
+	if err != nil {
+		log.Debug("Failed to increment user count: %v", err)
+		return strconv.FormatInt(0, 10), false
+	}
+	conn := GetPool().Get()
+	defer conn.Close()
+	str := account + "," + password
+	_, err_info := redis.String(conn.Do("SET", str, newUserId))
+	return strconv.FormatInt(newUserId, 10), err_info == nil
+}
+
+// 根据账号密码获取用户id
+func GetUserIDFromRedisByAP(account string, password string) (string, error) {
+	conn := GetPool().Get()
+	defer conn.Close()
+	str := account + "," + password
+	// 将字符串转换为字节切片
+	data := []byte(str)
+	encoded := base64.StdEncoding.EncodeToString(data)
+	userId, err := redis.String(conn.Do("HGET", encoded, "data"))
+
+	if err == redis.ErrNil {
+		// 如果 Redis 中没有数据
+		errorMsg := fmt.Sprintf("User data not found in Redis for userId: %s", userId)
+		log.Debug(errorMsg)
+		return "", errors.New(errorMsg)
+	} else if err != nil {
+		log.Debug("Error fetching user data from Redis: %v", err)
+		return "", err
+	}
+	return userId, nil
+}
+
+// 获取用户信息从 Redis 哈希表
+func GetUserInfoFromRedis(userId string) (*msg.UserInfo, error) {
+	// 获取 Redis 连接
+	conn := GetPool().Get()
+	defer conn.Close()
+
+	// 从 Redis 获取用户数据
+	userDataJson, err := redis.String(conn.Do("HGET", "user:"+userId, "data"))
+	if err == redis.ErrNil {
+		// 如果 Redis 中没有数据
+		errorMsg := fmt.Sprintf("User data not found in Redis for userId: %s", userId)
+		log.Debug(errorMsg)
+		return nil, errors.New(errorMsg)
+	} else if err != nil {
+		log.Debug("Error fetching user data from Redis: %v", err)
+		return nil, err
+	}
+
+	// 将获取到的 JSON 字符串反序列化为 UserData 对象
+	var userData msg.UserInfo
+	err = json.Unmarshal([]byte(userDataJson), &userData)
+	if err != nil {
+		log.Debug("Error unmarshalling user data from Redis: %v", err)
+		return nil, err
+	}
+
+	log.Debug("User data fetched from Redis for userId: %s", userId)
+	return &userData, nil
+}
+
+// // 字符串操作
+// _, err := conn.Do("SET", "key", "value")
+// val, err := redis.String(conn.Do("GET", "key"))
+
+// // 哈希操作
+// _, err := conn.Do("HSET", "user:1000", "name", "Alice", "age", 30)
+// name, err := redis.String(conn.Do("HGET", "user:1000", "name"))
+
+// // 列表操作
+// _, err := conn.Do("LPUSH", "mylist", "item1")
+// item, err := redis.String(conn.Do("RPOP", "mylist"))
+
+// // 过期时间
+// _, err := conn.Do("EXPIRE", "key", 3600)

+ 17 - 0
src/server/events/events.go

@@ -0,0 +1,17 @@
+package events
+
+import "github.com/name5566/leaf/gate"
+
+type EventType int
+
+const (
+	EventLudoMatch EventType = iota
+)
+
+type Event struct {
+	Type  EventType
+	Data  interface{}
+	Agent gate.Agent
+}
+
+var EventChan = make(chan Event, 100)

BIN
src/server/game/.DS_Store


+ 10 - 0
src/server/game/external.go

@@ -0,0 +1,10 @@
+package game
+
+import (
+	"server/game/internal"
+)
+
+var (
+	Module  = new(internal.Module)
+	ChanRPC = internal.ChanRPC
+)

+ 121 - 0
src/server/game/internal/chanrpc.go

@@ -0,0 +1,121 @@
+package internal
+
+import (
+	redismgr "server/db/redis"
+	"server/msg"
+	"server/user"
+	"time"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/log"
+)
+
+func init() {
+	skeleton.RegisterChanRPC("NewAgent", rpcNewAgent)
+	skeleton.RegisterChanRPC("CloseAgent", rpcCloseAgent)
+	skeleton.RegisterChanRPC("handleAuth", handleAuth)
+}
+
+func rpcNewAgent(args []interface{}) {
+	agent := args[0].(gate.Agent)
+	// 将新连接放入未认证池
+	pendingConns[agent] = &PendingConn{
+		Agent:      agent,
+		CreateTime: time.Now().Unix(),
+		Status:     ConnStatusUnauth,
+	}
+	go checkTimeout(agent)
+}
+
+func rpcCloseAgent(args []interface{}) {
+	log.Debug("rpcCloseAgent")
+
+	agent := args[0].(gate.Agent)
+	// 清理未认证连接
+	_, exists := pendingConns[agent]
+	if exists {
+		delete(pendingConns, agent)
+	}
+	// 清理已认证连接
+	for userID, a := range authedUsers {
+		if a == agent {
+			// a.SetUserData(&common.UserData{
+			// 	Id:       userID,
+			// 	Nickname: userID,
+			// 	Status:   0,
+			// })
+			delete(authedUsers, userID)
+			break
+		}
+	}
+}
+
+// 处理认证消息
+func handleAuth(args []interface{}) {
+	// 收到的登录消息
+	m := args[0].(*msg.ReqLogin)
+	// 消息的发送者
+	a := args[1].(gate.Agent)
+
+	log.Debug("handleAuth: nikeName=%v, userId=%v", m.Password, m.Account)
+	UserId, err := redismgr.GetUserIDFromRedisByAP(m.Account, m.Password)
+	if err != nil {
+		a.WriteMsg(&msg.ResLogin{
+			NikeName: "",
+			UserId:   "",
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 101,
+				ErrorMsg:  "Password or Account is error!",
+			},
+		})
+		return
+	}
+	// 找到未认证连接
+	if pending, exists := pendingConns[a]; exists {
+		// 认证成功,移动到已认证池
+		delete(pendingConns, a)
+		authedUsers[UserId] = a
+		pending.Status = ConnStatusAuthed
+
+		// 处理可能的重复登录
+		if oldAgent, exists := authedUsers[UserId]; exists && oldAgent != a {
+			oldAgent.Close()
+			delete(authedUsers, UserId)
+		}
+		// 获取用户信息 ,如果新用户则创建一个
+		userData := user.GetUserInfoById(UserId)
+		a.SetUserData(&msg.UserInfo{
+			UserId: userData.UserId,
+			MHead:  userData.MHead,
+			MCoin:  userData.MCoin,
+			Name:   userData.Name,
+			RoomId: userData.RoomId,
+		})
+
+		// 发送登录成功响应
+		// a.WriteMsg(&msg.ResLogin{
+		// 	NikeName: m.UserId,
+		// 	UserId:   UserId,
+		// })
+	}
+}
+
+// 定时检查超时连接
+func checkTimeout(agent gate.Agent) {
+	timeout := 30 * time.Second
+	timer := time.NewTimer(timeout)
+	defer timer.Stop()
+
+	select {
+	case <-timer.C:
+		// 检查是否仍在未认证池中
+		if conn, exists := pendingConns[agent]; exists {
+			if conn.Status == ConnStatusUnauth {
+				// 超时未认证,关闭连接
+				log.Debug("Connection timeout without auth, closing...")
+				agent.Close()
+				delete(pendingConns, agent)
+			}
+		}
+	}
+}

+ 38 - 0
src/server/game/internal/handler.go

@@ -0,0 +1,38 @@
+package internal
+
+import (
+	"reflect"
+	"server/msg"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/log"
+)
+
+func init() {
+	// Handler(&msg.ResPlayerOptAction{}, handlePlayerOptAction)
+	Handler(&msg.ResHeartBeat{}, handleHeartBeat)
+}
+
+func Handler(m interface{}, h interface{}) {
+	skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
+}
+
+func handlePlayerOptAction(args []interface{}) {
+	// m := args[0].(*msg.ResPlayerOptAction)
+	// a := args[1].(gate.Agent)
+	// switch m.GameId {
+	// case "teen_patti":
+	// 	events.EventChan <- events.Event{
+	// 		Type:  events.EventTeenPattiPlayerOptAction,
+	// 		Data:  m,
+	// 		Agent: a,
+	// 	}
+	// }
+}
+
+func handleHeartBeat(args []interface{}) {
+	m := args[0].(*msg.ResHeartBeat)
+	a := args[1].(gate.Agent)
+	log.Debug("handleHeartBeat: %s", m.Msg)
+	a.WriteMsg(&msg.ReqHeartBeat{Msg: "pong"})
+}

+ 57 - 0
src/server/game/internal/module.go

@@ -0,0 +1,57 @@
+package internal
+
+import (
+	"server/base"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/module"
+)
+
+const (
+	ConnStatusUnauth = iota // 未认证
+	ConnStatusAuthed        // 已认证
+)
+
+// 未认证连接管理
+type PendingConn struct {
+	Agent      gate.Agent
+	CreateTime int64
+	Status     int
+}
+
+var (
+	// 未认证连接池
+	pendingConns = make(map[gate.Agent]*PendingConn)
+	// 已认证用户
+	authedUsers = make(map[string]gate.Agent) // key: userID
+)
+
+var (
+	skeleton = base.NewSkeleton()
+	ChanRPC  = skeleton.ChanRPCServer
+)
+
+type Module struct {
+	*module.Skeleton
+}
+
+func (m *Module) OnInit() {
+	m.Skeleton = skeleton
+}
+
+func (m *Module) OnDestroy() {
+
+}
+
+func GetAgentByUserID(userID string) gate.Agent {
+	return authedUsers[userID]
+}
+
+func GetUserIDByAgent(agent gate.Agent) string {
+	for k, v := range authedUsers {
+		if v == agent {
+			return k
+		}
+	}
+	return ""
+}

+ 192 - 0
src/server/game/ludo/battle.go

@@ -0,0 +1,192 @@
+package ludo
+
+import (
+	"server/game"
+	"server/game/internal"
+	"server/msg"
+	"time"
+)
+
+type RoomInfoWrapper struct {
+	*msg.RoomInfo
+}
+
+func init() {
+	internal.Handler(&msg.SendColorSz{}, RecvPlayerSzNumber)
+
+	internal.Handler(&msg.SendRoleMove{}, RecvPlayerRoleMove)
+}
+
+// 清理解散的房间
+func ClearRoomInfoWrapperDisbandRoom() {
+	for k := range gameConfig.BattleRoom {
+		if gameConfig.BattleRoom[k].RoomStatus == msg.RoomStatus_END {
+			delete(gameConfig.BattleRoom, k)
+		}
+	}
+}
+
+func runBattle(room_info *RoomInfoWrapper) {
+	// cur_color_key := getCurColorEventKey(room_info)
+	// TimerManager.AddEvent(cur_color_key,)
+	gameConfig.BattleRoom[room_info.Id] = room_info
+	game.Module.AfterFunc(time.Second*time.Duration(3), func() {
+		room_info.notify_player_sz()
+	})
+}
+
+func getBattleRoomById(id int32) *RoomInfoWrapper {
+	room_info, exists := gameConfig.BattleRoom[id]
+	if !exists {
+		return nil
+	}
+	return room_info
+}
+
+// 根据玩家的骰子数进行相关处理
+func (room_info *RoomInfoWrapper) processSz(msg_body *msg.NotifyPlayerSzNumber) {
+	if msg_body.SzNumber == 6 {
+		canMoveList := room_info.getRolesBySzNumber(msg_body.Color, msg_body.SzNumber)
+		if len(canMoveList) > 0 {
+			room_info.NotifyToAllPlayer(msg_body)
+		} else {
+			room_info.setNextPlayerRound()
+			room_info.notify_player_sz()
+		}
+	} else {
+		notAtHomeRoles := room_info.getRoleNotAtHomeByColor(msg_body.Color)
+		if len(notAtHomeRoles) <= 0 { //全部都在家
+			room_info.setNextPlayerRound()
+			room_info.notify_player_sz()
+		} else if len(notAtHomeRoles) == 4 { //全部都从家里出来了
+			canMoveList := room_info.getCanMoveRoles(notAtHomeRoles, msg_body.SzNumber)
+			if len(canMoveList) > 0 {
+				room_info.notify_player_move(canMoveList)
+			} else {
+				room_info.setNextPlayerRound()
+				room_info.notify_player_sz()
+			}
+		} else {
+			if len(notAtHomeRoles) == 1 { //只有一个选择,那么直接让他走
+				//如果一个玩家离终点剩余的步骤不等于筛子数,也将会跳到下个人回合
+				if room_info.roleIsCanMove(notAtHomeRoles[0].MId, msg_body.SzNumber) {
+					room_info.send_role_move(msg_body.Color, notAtHomeRoles[0].MId)
+				} else {
+					room_info.setNextPlayerRound()
+					room_info.notify_player_sz()
+				}
+			} else {
+				canMoveList := room_info.getCanMoveRoles(notAtHomeRoles, msg_body.SzNumber)
+				if len(canMoveList) > 0 {
+					room_info.notify_player_move(canMoveList)
+				} else {
+					room_info.setNextPlayerRound()
+					room_info.notify_player_sz()
+				}
+			}
+		}
+	}
+}
+
+// 根据玩家走的步数进行相关处理
+func (room_info *RoomInfoWrapper) processMove(color msg.RoleType, szNumber int32, isAddRound bool, isFinish bool) {
+	if room_info.IsOverGame() { //宣布游戏结束
+		room_info.notify_settlement(color)
+	} else {
+		if szNumber == 6 || isAddRound || isFinish { //加一个回合
+
+		} else { //下个人回合
+			room_info.setNextPlayerRound()
+		}
+		room_info.notify_player_sz()
+	}
+}
+
+// 通知玩家结算
+func (room_info *RoomInfoWrapper) notify_settlement(color msg.RoleType) {
+	message := &msg.NotifySettlement{}
+	message.Color = color
+	message.FinishColors = room_info.getFinishColors()
+	room_info.NotifyToAllPlayer(message)
+}
+
+// 通知玩家开始移动角色
+func (room_info *RoomInfoWrapper) notify_player_move(list []*msg.RoleData) {
+	cur_color := room_info.CurRoundColor
+	if len(list) == 0 {
+		room_info.send_role_move(cur_color, list[0].MId)
+	} else {
+		if room_info.containsRobot(cur_color) {
+			//如果是机器人,思考一秒然后执行移动角色
+			room_info.delayActionMoveRobot(1000, cur_color, list)
+		}
+	}
+
+	message := &msg.NotifyPlayerOpt{
+		Color:        room_info.CurRoundColor,
+		Opt:          msg.OptType_SELECT_ROLE,
+		CanMoveRoles: list,
+	}
+	room_info.NotifyToAllPlayer(message)
+}
+
+// 通知玩家状态
+func (room_info *RoomInfoWrapper) notify_player_status(color msg.RoleType, status msg.PlayerStatus, colors []*msg.ColorData) {
+	message := &msg.NotifyPlayerStatus{
+		Color:  color,
+		Status: status,
+		Colors: colors,
+	}
+	room_info.NotifyToAllPlayer(message)
+}
+
+// 通知玩家要骰子
+func (room_info *RoomInfoWrapper) notify_player_sz() {
+	cur_color := room_info.CurRoundColor
+	message := &msg.NotifyPlayerOpt{
+		Color: cur_color,
+		Opt:   msg.OptType_ZHI_SHAI_ZI,
+	}
+	room_info.NotifyToAllPlayer(message)
+}
+
+// 获取房间ID
+func (room_info *RoomInfoWrapper) GetRoomId() int32 {
+	return room_info.Id
+}
+
+// 房间里通知所有玩家
+func (room_info *RoomInfoWrapper) NotifyToAllPlayer(message interface{}) {
+	for i := range room_info.Colors {
+		user_id := room_info.Colors[i].MId
+		user_agent := getAgentByUserId(user_id)
+		if user_agent != nil {
+			user_agent.WriteMsg(message)
+		}
+	}
+}
+
+// 给前端展示一段时间后,再操作
+func (room_info *RoomInfoWrapper) delayActionNextRound(td int32, color msg.RoleType, szNumber int32, isAddRound bool, isFinish bool) {
+	dtn := time.Duration(td) * time.Microsecond
+	game.Module.Skeleton.AfterFunc(dtn, func() {
+		room_info.processMove(color, szNumber, isAddRound, isFinish)
+	})
+}
+
+// 给前端展示一段时间后,再操作
+func (room_info *RoomInfoWrapper) delayActionSz(td int, msg_body *msg.NotifyPlayerSzNumber) {
+	dtn := time.Duration(td) * time.Microsecond
+	game.Module.Skeleton.AfterFunc(dtn, func() {
+		room_info.processSz(msg_body)
+	})
+}
+
+// 延迟后,移动机器人
+func (room_info *RoomInfoWrapper) delayActionMoveRobot(td int, color msg.RoleType, list []*msg.RoleData) {
+	dtn := time.Duration(td) * time.Microsecond
+	game.Module.Skeleton.AfterFunc(dtn, func() {
+		role := getRandomItem(list)
+		room_info.send_role_move(color, role.MId)
+	})
+}

+ 36 - 0
src/server/game/ludo/battle_manager.go

@@ -0,0 +1,36 @@
+package ludo
+
+import (
+	"crypto/rand"
+	"math/big"
+	"server/msg"
+)
+
+func randomSz() int32 {
+	// 生成0-5的随机数
+	n, err := rand.Int(rand.Reader, big.NewInt(6))
+	if err != nil {
+		panic(err) // 在实际应用中应该更优雅地处理错误
+	}
+	// 转换为int32并加1得到1-6的范围
+	return int32(n.Int64()) + 1
+}
+func roleIndexOf(arr []*msg.ColorData, target *msg.ColorData) int {
+	for i, v := range arr {
+		if v == target { // 比较指针地址
+			return i
+		}
+	}
+	return -1 // 未找到
+}
+
+func getRandomItem[T any](slice []T) T {
+	if len(slice) == 0 {
+		var zero T
+		return zero
+	}
+
+	// 生成加密安全的随机数
+	n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(slice))))
+	return slice[n.Int64()]
+}

+ 75 - 0
src/server/game/ludo/color_recv.go

@@ -0,0 +1,75 @@
+package ludo
+
+import (
+	"server/msg"
+
+	"github.com/name5566/leaf/gate"
+)
+
+// 接受玩家移动消息
+func RecvPlayerRoleMove(args []interface{}) {
+	m := args[0].(*msg.SendRoleMove)
+	a := args[1].(gate.Agent)
+	ud := a.UserData()
+	room_id := ud.(*msg.UserInfo).RoomId
+	color := m.Color
+	if room_id == 0 {
+		a.WriteMsg(&msg.NotifyPlayerSzNumber{
+			Color: color,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 101,
+				ErrorMsg:  "玩家不在房间!",
+			},
+		})
+	} else {
+		room_info := getBattleRoomById(room_id)
+
+		if room_info != nil {
+			room_info.send_role_move(m.Color, m.RoleId)
+		}
+
+	}
+}
+
+// 接受玩家摇筛子消息
+func RecvPlayerSzNumber(args []interface{}) {
+	m := args[0].(*msg.SendColorSz)
+	a := args[1].(gate.Agent)
+	ud := a.UserData()
+	room_id := ud.(*msg.UserInfo).RoomId
+	color := m.Color
+	if room_id == 0 {
+		a.WriteMsg(&msg.NotifyPlayerSzNumber{
+			Color: color,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 101,
+				ErrorMsg:  "玩家不在房间!",
+			},
+		})
+	} else {
+		room_info := getBattleRoomById(room_id)
+
+		if room_info != nil {
+
+			sz_number := randomSz()
+
+			round_data := &msg.Round{
+				MColor:   color,
+				SzNumber: sz_number,
+				Opt:      msg.OptType_ZHI_SHAI_ZI,
+			}
+
+			room_info.addRound(round_data)
+
+			msg_body := &msg.NotifyPlayerSzNumber{
+				Color:    color,
+				SzNumber: sz_number,
+			}
+
+			room_info.NotifyToAllPlayer(msg_body)
+			room_info.delayActionSz(1000, msg_body)
+		}
+
+	}
+
+}

+ 73 - 0
src/server/game/ludo/color_send.go

@@ -0,0 +1,73 @@
+package ludo
+
+import (
+	"server/msg"
+)
+
+// 发送消息移动角色
+func (room_info *RoomInfoWrapper) send_role_move(color msg.RoleType, role_id string) {
+	role_data := room_info.getRoleDataById(role_id)
+	szNumber := room_info.getLastRoundByOpt(msg.OptType_ZHI_SHAI_ZI).SzNumber
+	step_data := &msg.MoveStepData{
+		MId: role_id,
+	}
+	message := &msg.NotifyPlayerMove{
+		Color: color,
+		Step:  step_data,
+	}
+	des_step := role_data.Step + szNumber
+	if role_data.Step == 0 {
+		des_step = role_data.Step + 1
+	}
+	road_key := room_info.getMapPosByColorAndStep(color, des_step)
+	des_road_roles := room_info.getRoadRoles(road_key)
+	if len(des_road_roles) > 0 { //如果路上还有其他人
+		if room_info.isWuDiRoad(road_key) { //如果是无敌的路
+
+		} else {
+			enemys := room_info.getRolesByColorRoles(color, des_road_roles)
+			if len(enemys) > 0 { //说明目标路上有其他敌人
+				//将所有敌人踢回阵营,然后再加一轮筛子操作
+				room_info.tickRolesBackHome(enemys)
+				message.Kick = enemys
+			}
+		}
+	}
+
+	isFinish := false
+	isFinishThree := false
+	if room_info.moveTargetIsEnd(color, road_key) { //如果是终点,奖励一次筛子
+		isFinishThree = len(room_info.getFinishRolesByColor(color)) == 3
+		if isFinishThree { //这个玩家跑完
+			room_info.addFinishColor(color)
+			room_info.notify_player_status(color, msg.PlayerStatus_COLOR_FINISH, room_info.getFinishColors())
+		} else {
+			//奖励他一次筛子
+			isFinish = true
+		}
+	}
+	message.Step.Step = des_step
+	message.Step.OldSetp = role_data.Step
+	room_info.updateRoleStep(role_id, message.Step.Step)
+
+	room_info.NotifyToAllPlayer(message)
+
+	var kick_delay_time int32
+
+	if len(message.Kick) > 0 {
+		max_oldSetp := message.Kick[0].OldSetp
+		for i := 0; i < len(message.Kick); i++ {
+			element := message.Kick[i]
+			if element.OldSetp > max_oldSetp {
+				max_oldSetp = element.OldSetp
+			}
+		}
+		kick_delay_time = kick_delay_time + max_oldSetp*100
+	}
+
+	delay_time := szNumber*300 + kick_delay_time
+
+	isAddRound := len(message.Kick) > 0
+
+	room_info.delayActionNextRound(delay_time, color, szNumber, isAddRound, isFinish)
+}

+ 30 - 0
src/server/game/ludo/event.go

@@ -0,0 +1,30 @@
+package ludo
+
+import (
+	"log"
+	"server/events"
+	"server/game/internal"
+	"server/msg"
+)
+
+func handleEvents() {
+	for event := range events.EventChan {
+		switch event.Type {
+		case events.EventLudoMatch:
+			userId := internal.GetUserIDByAgent(event.Agent)
+			if userId != "" {
+				if matchData, ok := event.Data.(*msg.MatchLudo); ok {
+					matchRoom(matchData, userId, event.Agent)
+				} else {
+					// Handle type assertion error
+					log.Printf("invalid match data type: %T", event.Data)
+				}
+			}
+
+		}
+	}
+}
+
+func init() {
+
+}

+ 109 - 0
src/server/game/ludo/ludo.go

@@ -0,0 +1,109 @@
+package ludo
+
+import (
+	"server/game/internal"
+	"server/msg"
+	"server/tools"
+	"sync"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/log"
+)
+
+// GameRule 定义游戏规则
+type GameRule struct {
+	MAX_STEP     int32
+	WU_DI_ROAD   []string
+	AN_QUAN_ROAD []string
+}
+
+type Config struct {
+	RoomMap    map[int32]*msg.RoomInfo // Key is room ID
+	mu         sync.RWMutex            // Protects RoomMap access
+	BattleRoom map[int32]*RoomInfoWrapper
+}
+
+var (
+	gameConfig *Config
+	gameRule   = GameRule{
+		MAX_STEP: 57,
+		WU_DI_ROAD: []string{
+			"p_2", "p_15", "p_28", "p_41", "p_23", "p_10", "p_36", "p_49",
+			"red_1", "red_2", "red_3", "red_4", "red_5", "red_des",
+			"green_1", "green_2", "green_3", "green_4", "green_5", "green_des",
+			"yellow_1", "yellow_2", "yellow_3", "yellow_4", "yellow_5", "yellow_des",
+			"blue_1", "blue_2", "blue_3", "blue_4", "blue_5", "blue_des",
+		},
+		AN_QUAN_ROAD: []string{
+			"p_2", "p_15", "p_28", "p_41", "p_23", "p_10", "p_36", "p_49",
+		},
+	}
+
+	redRoad = []string{
+		"nil", "p_2", "p_3", "p_4", "p_5", "p_6", "p_7", "p_8", "p_9", "p_10",
+		"p_11", "p_12", "p_13", "p_14", "p_15", "p_16", "p_17", "p_18",
+		"p_19", "p_20", "p_21", "p_22", "p_23", "p_24", "p_25", "p_26",
+		"p_27", "p_28", "p_29", "p_30", "p_31", "p_32", "p_33", "p_34",
+		"p_35", "p_36", "p_37", "p_38", "p_39", "p_40", "p_41", "p_42",
+		"p_43", "p_44", "p_45", "p_46", "p_47", "p_48", "p_49", "p_50",
+		"p_51", "p_52", "red_1", "red_2", "red_3", "red_4", "red_5", "red_des",
+	}
+
+	greenRoad = []string{
+		"nil", "p_15", "p_16", "p_17", "p_18", "p_19", "p_20", "p_21", "p_22",
+		"p_23", "p_24", "p_25", "p_26", "p_27", "p_28", "p_29", "p_30",
+		"p_31", "p_32", "p_33", "p_34", "p_35", "p_36", "p_37", "p_38",
+		"p_39", "p_40", "p_41", "p_42", "p_43", "p_44", "p_45", "p_46",
+		"p_47", "p_48", "p_49", "p_50", "p_51", "p_52", "p_53", "p_2",
+		"p_3", "p_4", "p_5", "p_6", "p_7", "p_8", "p_9", "p_10", "p_11",
+		"p_12", "p_13", "green_1", "green_2", "green_3", "green_4", "green_5", "green_des",
+	}
+
+	yellowRoad = []string{
+		"nil", "p_28", "p_29", "p_30", "p_31", "p_32", "p_33", "p_34", "p_35",
+		"p_36", "p_37", "p_38", "p_39", "p_40", "p_41", "p_42", "p_43",
+		"p_44", "p_45", "p_46", "p_47", "p_48", "p_49", "p_50", "p_51",
+		"p_52", "p_53", "p_2", "p_3", "p_4", "p_5", "p_6", "p_7", "p_8",
+		"p_9", "p_10", "p_11", "p_12", "p_13", "p_14", "p_15", "p_16",
+		"p_17", "p_18", "p_19", "p_20", "p_21", "p_22", "p_23", "p_24",
+		"p_25", "p_26", "yellow_1", "yellow_2", "yellow_3", "yellow_4", "yellow_5", "yellow_des",
+	}
+
+	blueRoad = []string{
+		"nil", "p_41", "p_42", "p_43", "p_44", "p_45", "p_46", "p_47", "p_48",
+		"p_49", "p_50", "p_51", "p_52", "p_53", "p_2", "p_3", "p_4",
+		"p_5", "p_6", "p_7", "p_8", "p_9", "p_10", "p_11", "p_12", "p_13",
+		"p_14", "p_15", "p_16", "p_17", "p_18", "p_19", "p_20", "p_21",
+		"p_22", "p_23", "p_24", "p_25", "p_26", "p_27", "p_28", "p_29",
+		"p_30", "p_31", "p_32", "p_33", "p_34", "p_35", "p_36", "p_37",
+		"p_38", "p_39", "blue_1", "blue_2", "blue_3", "blue_4", "blue_5", "blue_des",
+	}
+)
+
+var TimerManager = tools.NewTimerManager()
+
+func InitGame() {
+	gameConfig = &Config{
+		RoomMap: make(map[int32]*msg.RoomInfo),
+	}
+	go handleEvents()
+}
+
+func startGame(room_info *msg.RoomInfo) {
+	room_info.RoomStatus = msg.RoomStatus_START //状态设置为开始游戏状态
+	//给每个玩家分配阵营初始化角色
+	initRoles(room_info)
+	//设置第一个操作的阵营
+	setFirstColor(room_info)
+	// player_count := getPlayerCount(room_info)
+	runBattle(&RoomInfoWrapper{room_info})
+}
+
+func getAgentByUserId(userId string) gate.Agent {
+	ag := internal.GetAgentByUserID(userId)
+	if ag == nil {
+		log.Error("agent is nil userId = %s ", userId)
+		return nil
+	}
+	return ag
+}

+ 269 - 0
src/server/game/ludo/ludo_room_data.go

@@ -0,0 +1,269 @@
+package ludo
+
+import (
+	"server/msg"
+	"strings"
+
+	"github.com/name5566/leaf/log"
+)
+
+// 添加完成的玩家
+func (room_info *RoomInfoWrapper) addFinishColor(color msg.RoleType) {
+	color_data := room_info.getColorDataByColor(color)
+	color_data.IsFinish = true
+	room_info.FinishColors = append(room_info.FinishColors, color_data)
+}
+
+// 根据颜色获取阵容数据
+func (room_info *RoomInfoWrapper) getColorDataByColor(color msg.RoleType) *msg.ColorData {
+	for i := 0; i < len(room_info.Colors); i++ {
+		element := room_info.Colors[i]
+		if element.MColor == color {
+			return element
+		}
+	}
+	return nil
+}
+
+// 获取完成的阵营
+func (room_info *RoomInfoWrapper) getFinishColors() []*msg.ColorData {
+	return room_info.FinishColors
+}
+
+// 更新角色位置
+func (room_info *RoomInfoWrapper) updateRoleStep(role_id string, step int32) {
+	role_data := room_info.getRoleDataById(role_id)
+	role_data.OldSetp = role_data.Step
+	role_data.Step = step
+}
+
+// 是否结束游戏
+func (room_info *RoomInfoWrapper) IsOverGame() bool {
+	colors := room_info.Colors
+	count := 0
+	for i := 0; i < len(room_info.Colors); i++ {
+		element := room_info.Colors[i]
+		if element.IsFinish || element.IsKick {
+			count++
+		}
+	}
+	return count >= len(colors)-1
+}
+
+func (room_info *RoomInfoWrapper) getColors() []*msg.ColorData {
+	return room_info.Colors
+}
+
+// 开始下个玩家的回合
+func (room_info *RoomInfoWrapper) setNextPlayerRound() {
+	room_info.CurRoundColor = room_info.findNextColor()
+	if room_info.CurRoundColor == msg.RoleType_ROLE_TYPE_UNKNOWN {
+		log.Error("setNextPlayerRound error")
+	}
+}
+
+// 找到下一个没有完成的玩家
+func (room_info *RoomInfoWrapper) findNextColor() msg.RoleType {
+	cur := room_info.CurRoundColor
+	colors := room_info.getColors()
+
+	i := roleIndexOf(colors, room_info.getColorDataByColor(cur))
+	if i == (len(colors) - 1) {
+		i = 0
+	} else {
+		i = (i + 1)
+	}
+	for index := 0; index < len(colors); index++ {
+		element := colors[index]
+		if !element.IsFinish && !element.IsKick {
+			return element.MColor
+		}
+	}
+
+	for index := 0; index < i; index++ {
+		element := colors[index]
+		if !element.IsFinish && !element.IsKick {
+			return element.MColor
+		}
+	}
+
+	return msg.RoleType_ROLE_TYPE_UNKNOWN
+}
+
+// 当前玩家是否是机器人
+func (room_info *RoomInfoWrapper) containsRobot(color msg.RoleType) bool {
+	color_data := room_info.getColorDataByColor(color)
+	return strings.Contains(color_data.MId, "robot")
+}
+
+// 获取一个颜色的玩家有多少个*不在家*的角色
+func (room_info *RoomInfoWrapper) getRoleNotAtHomeByColor(color msg.RoleType) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 4)
+	roles := room_info.Roles
+	for i := 0; i < len(roles); i++ {
+		element := roles[i]
+		if element.MColor == color && element.Step != 0 {
+			temp = append(temp, element)
+		}
+	}
+	return temp
+}
+
+// 根据操作获取最后一个操作回合数据
+func (room_info *RoomInfoWrapper) getLastRoundByOpt(opt msg.OptType) *msg.Round {
+	for i := 0; i < len(room_info.Rounds); i++ {
+		element := room_info.Rounds[i]
+		if opt == element.Opt {
+			return element
+		}
+	}
+	return nil
+}
+
+// 获取地图位置
+func (room_info *RoomInfoWrapper) getMapPosByColorAndStep(color msg.RoleType, step int32) string {
+	if color == msg.RoleType_RED {
+		return redRoad[step]
+	} else if color == msg.RoleType_GREEN {
+		return greenRoad[step]
+	} else if color == msg.RoleType_BLUE {
+		return blueRoad[step]
+	} else if color == msg.RoleType_YELLOW {
+		return yellowRoad[step]
+	}
+	return ""
+}
+
+// 获取目标路上所有角色
+func (room_info *RoomInfoWrapper) getRoadRoles(key1 string) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 10) // 初始长度 0,容量 10
+	for i := 0; i < len(room_info.Roles); i++ {
+		element := room_info.Roles[i]
+		role_road_key := room_info.getMapPosByColorAndStep(element.MColor, element.Step)
+		if key1 == role_road_key {
+			temp = append(temp, element)
+		}
+	}
+	return temp
+}
+
+// 是否是无敌的点
+func (room_info *RoomInfoWrapper) isWuDiRoad(key1 string) bool {
+	list := gameRule.WU_DI_ROAD
+	for i := 0; i < len(list); i++ {
+		element := list[i]
+		if key1 == element {
+			return true
+		}
+	}
+	return false
+}
+
+// 判断目标非自己阵营的角色
+func (room_info *RoomInfoWrapper) getRolesByColorRoles(color msg.RoleType, roles []*msg.RoleData) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 10) // 初始长度 0,容量 10
+	for i := range roles {
+		element := roles[i]
+		if element.MColor != color {
+			temp = append(temp, element)
+		}
+	}
+	return temp
+}
+
+// 将一批角色踢回家
+func (room_info *RoomInfoWrapper) tickRolesBackHome(roles []*msg.RoleData) {
+	for i := range roles {
+		role_data := roles[i]
+		role_data.Step = 0
+	}
+}
+
+// 移动的目标是否是终点
+func (room_info *RoomInfoWrapper) moveTargetIsEnd(color msg.RoleType, key1 string) bool {
+	if color == msg.RoleType_BLUE {
+		return key1 == "blue_des"
+	} else if color == msg.RoleType_YELLOW {
+		return key1 == "yellow_des"
+	} else if color == msg.RoleType_GREEN {
+		return key1 == "green_des"
+	} else if color == msg.RoleType_RED {
+		return key1 == "red_des"
+	}
+	return false
+}
+
+// 根据颜色获取有多少个完成竞赛的角色
+func (room_info *RoomInfoWrapper) getFinishRolesByColor(color msg.RoleType) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 10)
+	if color == msg.RoleType_BLUE {
+		temp = room_info.getRoadRoles("blue_des")
+	} else if color == msg.RoleType_YELLOW {
+		temp = room_info.getRoadRoles("yellow_des")
+	} else if color == msg.RoleType_GREEN {
+		temp = room_info.getRoadRoles("green_des")
+	} else if color == msg.RoleType_RED {
+		temp = room_info.getRoadRoles("red_des")
+	}
+	return temp
+}
+
+// 根据颜色获取对应颜色的所有角色
+func (room_info *RoomInfoWrapper) getRoleDataByColor(color msg.RoleType) []*msg.RoleData {
+	list := make([]*msg.RoleData, 0, 4) // 初始长度 0,容量 10
+	for _, v := range room_info.Roles {
+		if v.MColor == color {
+			list = append(list, v)
+		}
+	}
+	return list
+}
+
+// 根据id获取角色
+func (room_info *RoomInfoWrapper) getRoleDataById(role_id string) *msg.RoleData {
+	for i := 0; i < len(room_info.Roles); i++ {
+		element := room_info.Roles[i]
+		if element.MId == role_id {
+			return element
+		}
+	}
+	return nil
+}
+
+// 记录每个回合
+func (room_info *RoomInfoWrapper) addRound(rd *msg.Round) {
+	room_info.Rounds = append(room_info.Rounds, rd)
+}
+
+// 根据骰子数获取角色列表
+func (room_info *RoomInfoWrapper) getRolesBySzNumber(color msg.RoleType, szNumber int32) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 4) // 初始长度 0,容量 10
+	list := room_info.getRoleDataByColor(color)
+	for i := 0; i < len(list); i++ {
+		element := list[i]
+		des_step := element.Step + szNumber
+		if des_step < gameRule.MAX_STEP {
+			temp = append(temp, element)
+		}
+	}
+	return temp
+}
+
+// 根据筛子数判定角色是否可以移动
+func (room_info *RoomInfoWrapper) roleIsCanMove(role_id string, szNumber int32) bool {
+	role_data := room_info.getRoleDataById(role_id)
+	des_step := role_data.MSeat + szNumber
+	return des_step <= gameRule.MAX_STEP
+}
+
+// 获取可以移动的玩家
+func (room_info *RoomInfoWrapper) getCanMoveRoles(roles []*msg.RoleData, szNumber int32) []*msg.RoleData {
+	temp := make([]*msg.RoleData, 0, 4)
+	for i := 0; i < len(roles); i++ {
+		element := roles[i]
+		if room_info.roleIsCanMove(element.MId, szNumber) {
+			temp = append(temp, element)
+		}
+	}
+	return temp
+}

+ 43 - 0
src/server/game/ludo/match.go

@@ -0,0 +1,43 @@
+package ludo
+
+import (
+	"server/msg"
+
+	"github.com/name5566/leaf/gate"
+)
+
+//根据玩家要匹配的对局,给玩家寻找和的房间
+
+func matchRoom(matchInfo *msg.MatchLudo, userId string, user_agent gate.Agent) {
+	roomList := GetRoomsByLevel(matchInfo, msg.RoomStatus_AWAIT)
+
+	if len(roomList) == 0 {
+		// Create new room if none available
+		room_info := CreateRoom(matchInfo, userId)
+		if room_info == nil {
+			sendMatchErrorMsg(user_agent, 101, "not find player")
+		}
+	} else {
+		// 如果房间具备开始的条件
+		isFullPlayer, err_info := JoinRoom(roomList[0], matchInfo, userId)
+		if err_info != nil {
+			if isFullPlayer {
+				startGame(roomList[0])
+			}
+		} else {
+			sendMatchErrorMsg(user_agent, 101, "not find player")
+		}
+	}
+}
+
+func sendMatchErrorMsg(user_agent gate.Agent, error_code int32, msg_str string) {
+	if user_agent != nil {
+		user_agent.WriteMsg(&msg.ResMatchLudo{
+			Success: false,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: error_code,
+				ErrorMsg:  msg_str,
+			},
+		})
+	}
+}

+ 191 - 0
src/server/game/ludo/room.go

@@ -0,0 +1,191 @@
+package ludo
+
+import (
+	"errors"
+	"fmt"
+	redismgr "server/db/redis"
+	"server/msg"
+)
+
+// 判断是否已存在选择的阵营
+func IsMatchColor(room_info *msg.RoomInfo, select_color msg.RoleType) bool {
+	for c := range room_info.Colors {
+		if room_info.Colors[c].MColor == select_color {
+			return false
+		}
+	}
+	return true
+}
+func AddRoom(room *msg.RoomInfo) {
+	gameConfig.mu.Lock()
+	defer gameConfig.mu.Unlock()
+	gameConfig.RoomMap[room.Id] = room
+}
+
+func RemoveRoom(roomID int32) {
+	gameConfig.mu.Lock()
+	defer gameConfig.mu.Unlock()
+	delete(gameConfig.RoomMap, roomID)
+}
+
+func GetRoom(roomID int32) *msg.RoomInfo {
+	gameConfig.mu.RLock()
+	defer gameConfig.mu.RUnlock()
+	return gameConfig.RoomMap[roomID]
+}
+
+func GetRoomsByType(roomType msg.RoomType) []*msg.RoomInfo {
+	gameConfig.mu.RLock()
+	defer gameConfig.mu.RUnlock()
+
+	var result []*msg.RoomInfo
+	for _, room := range gameConfig.RoomMap {
+		if room.RoomType == roomType {
+			result = append(result, room)
+		}
+	}
+	return result
+}
+
+func GetRoomsByLevel(matchInfo *msg.MatchLudo, roomStatus msg.RoomStatus) []*msg.RoomInfo {
+	gameConfig.mu.Lock()
+	defer gameConfig.mu.Unlock()
+	roomLevel := matchInfo.RoomLevel
+	color := matchInfo.SelectColor
+	roomType := matchInfo.SelectRoomType
+	var result []*msg.RoomInfo
+	for _, room := range gameConfig.RoomMap {
+		if roomType == room.RoomType && room.RoomLevel == roomLevel && room.RoomStatus == roomStatus && IsMatchColor(room, color) {
+			result = append(result, room)
+		}
+	}
+	return result
+}
+
+func CreateRoom(matchInfo *msg.MatchLudo, userId string) *msg.RoomInfo {
+	gameConfig.mu.Lock()
+	defer gameConfig.mu.Unlock()
+
+	userInfo, err_info := redismgr.GetUserInfoFromRedis(userId)
+	if err_info != nil {
+		return nil
+	}
+	newRoom := &msg.RoomInfo{
+		Id:         generateRoomID(),
+		RoomLevel:  int32(matchInfo.RoomLevel),
+		RoomType:   matchInfo.SelectRoomType,
+		RoomMode:   msg.RoomMode_WAN_JIA, //设置为玩家pvp模式
+		RoomStatus: msg.RoomStatus_AWAIT,
+		Colors: []*msg.ColorData{
+			{
+				MId:        userId,
+				MColor:     matchInfo.SelectColor, //所属阵营
+				IsKick:     false,                 //是否被踢
+				IsFinish:   false,                 //是否完成了比赛
+				TimeOutNum: 0,                     //超时次数
+				MName:      userInfo.Name,         //玩家名字
+				MHead:      userInfo.MHead,        //头像
+				RankNum:    0,                     //本局游戏的名次
+				MCoin:      userInfo.MCoin,        //玩家拥有的金币
+			},
+		},
+	}
+	gameConfig.RoomMap[newRoom.Id] = newRoom
+	return newRoom
+}
+
+func JoinRoom(room *msg.RoomInfo, matchInfo *msg.MatchLudo, userId string) (bool, error) {
+	gameConfig.mu.Lock()
+	defer gameConfig.mu.Unlock()
+	userInfo, err_info := redismgr.GetUserInfoFromRedis(userId)
+	if err_info != nil {
+		return false, errors.New("not find user at JoinRoom time ")
+	}
+	user_agent := getAgentByUserId(userId)
+	if user_agent == nil {
+		return false, errors.New("not find user ")
+	}
+
+	room.Colors = append(room.Colors, &msg.ColorData{
+		MId:        userId,
+		MColor:     matchInfo.SelectColor, //所属阵营
+		IsKick:     false,                 //是否被踢
+		IsFinish:   false,                 //是否完成了比赛
+		TimeOutNum: 0,                     //超时次数
+		MName:      userInfo.Name,         //玩家名字
+		MHead:      userInfo.MHead,        //头像
+		RankNum:    0,                     //本局游戏的名次
+		MCoin:      userInfo.MCoin,        //玩家拥有的金币
+	})
+
+	ud := &msg.UserInfo{
+		UserId: userInfo.UserId,
+		Name:   userInfo.Name,
+		MCoin:  userInfo.MCoin,
+		MHead:  userInfo.MHead,
+		RoomId: room.Id,
+	}
+
+	redismgr.SaveUserInfoToRedis(ud)
+
+	user_agent.SetUserData(ud)
+
+	isFullPlayer := false
+
+	if room.RoomType == msg.RoomType_SHUANG_REN { //说明是两人的房间
+		isFullPlayer = len(room.Colors) == 2
+	} else if room.RoomType == msg.RoomType_SIREN_REN { //说明是四人的房间
+		isFullPlayer = len(room.Colors) == 4
+	}
+	return isFullPlayer, nil
+}
+
+func generateRoomID() int32 {
+	gameConfig.mu.RLock()
+	defer gameConfig.mu.RUnlock()
+	return int32(len(gameConfig.RoomMap) + 1)
+}
+
+func getPlayerCount(room_info *msg.RoomInfo) int {
+	if room_info.RoomType == msg.RoomType_SHUANG_REN {
+		return 2
+	}
+	return 4
+}
+
+func setPlayersRoomId(room_info *msg.RoomInfo) {
+	for i := range room_info.Colors {
+		user_id := room_info.Colors[i].MId
+		user_agent := getAgentByUserId(user_id)
+		if user_agent != nil {
+			user_agent.SetUserData(&msg.UserInfo{})
+		}
+	}
+}
+
+func initRoles(room_info *msg.RoomInfo) {
+	for i := range room_info.Colors {
+		color_data := room_info.Colors[i]
+		color := color_data.MColor
+		for i := range 4 {
+			room_info.Roles = append(room_info.Roles, &msg.RoleData{
+				MColor:   color,
+				MSeat:    int32(i),
+				MId:      fmt.Sprintf("%d_%d", color, i),
+				MCurRoad: msg.RoadType_HOME,
+			})
+		}
+
+	}
+}
+
+func setFirstColor(room_info *msg.RoomInfo) {
+	// if room_info.RoomType == msg.RoomType_SHUANG_REN {
+	// 	room_info.CurRoundColor =
+	// }
+	room_info.CurRoundColor = room_info.Colors[0].MColor
+}
+
+func getCurColorEventKey(room_info *msg.RoomInfo) string {
+	return fmt.Sprintf("%d_%s", room_info.Id, room_info.CurRoundColor)
+}

+ 21 - 0
src/server/gamedata/reader.go

@@ -0,0 +1,21 @@
+package gamedata
+
+import (
+	"github.com/name5566/leaf/log"
+	"github.com/name5566/leaf/recordfile"
+	"reflect"
+)
+
+func readRf(st interface{}) *recordfile.RecordFile {
+	rf, err := recordfile.New(st)
+	if err != nil {
+		log.Fatal("%v", err)
+	}
+	fn := reflect.TypeOf(st).Name() + ".txt"
+	err = rf.Read("gamedata/" + fn)
+	if err != nil {
+		log.Fatal("%v: %v", fn, err)
+	}
+
+	return rf
+}

+ 9 - 0
src/server/gate/external.go

@@ -0,0 +1,9 @@
+package gate
+
+import (
+	"server/gate/internal"
+)
+
+var (
+	Module = new(internal.Module)
+)

+ 29 - 0
src/server/gate/internal/module.go

@@ -0,0 +1,29 @@
+package internal
+
+import (
+	"github.com/name5566/leaf/gate"
+	"server/conf"
+	"server/game"
+	"server/msg"
+)
+
+type Module struct {
+	*gate.Gate
+}
+
+func (m *Module) OnInit() {
+	m.Gate = &gate.Gate{
+		MaxConnNum:      conf.Server.MaxConnNum,
+		PendingWriteNum: conf.PendingWriteNum,
+		MaxMsgLen:       conf.MaxMsgLen,
+		WSAddr:          conf.Server.WSAddr,
+		HTTPTimeout:     conf.HTTPTimeout,
+		CertFile:        conf.Server.CertFile,
+		KeyFile:         conf.Server.KeyFile,
+		TCPAddr:         conf.Server.TCPAddr,
+		LenMsgLen:       conf.LenMsgLen,
+		LittleEndian:    conf.LittleEndian,
+		Processor:       msg.Processor,
+		AgentChanRPC:    game.ChanRPC,
+	}
+}

+ 14 - 0
src/server/gate/router.go

@@ -0,0 +1,14 @@
+package gate
+
+import (
+	"server/game"
+	"server/hall"
+	"server/login"
+	"server/msg"
+)
+
+func init() {
+	msg.Processor.SetRouter(&msg.ResLogin{}, login.ChanRPC)
+	msg.Processor.SetRouter(&msg.MatchLudo{}, hall.ChanRPC)
+	msg.Processor.SetRouter(&msg.ResHeartBeat{}, game.ChanRPC)
+}

+ 19 - 0
src/server/go.mod

@@ -0,0 +1,19 @@
+module server
+
+go 1.23.1
+
+require (
+	github.com/go-sql-driver/mysql v1.8.1
+	github.com/gomodule/redigo v2.0.0+incompatible
+	github.com/name5566/leaf v0.0.0-20221021105039-af71eb082cda
+	google.golang.org/protobuf v1.36.2
+	gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
+)
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
+	github.com/gorilla/websocket v1.5.3 // indirect
+	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
+)

+ 30 - 0
src/server/go.sum

@@ -0,0 +1,30 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/name5566/leaf v0.0.0-20221021105039-af71eb082cda h1:5S+9luohX8Whu/4VreRe4E0bHSSuJ4hyIGJc1nvNQzQ=
+github.com/name5566/leaf v0.0.0-20221021105039-af71eb082cda/go.mod h1:JrOIxq3vDxvtuEI7Kmm2yqkuBfuT9DMLFMnCyYHLaKM=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
+google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

+ 10 - 0
src/server/hall/external.go

@@ -0,0 +1,10 @@
+package hall
+
+import (
+	"server/hall/internal"
+)
+
+var (
+	Module  = new(internal.Module)
+	ChanRPC = internal.ChanRPC
+)

+ 67 - 0
src/server/hall/internal/handler.go

@@ -0,0 +1,67 @@
+package internal
+
+import (
+	"reflect"
+	redismgr "server/db/redis"
+	"server/events"
+	"server/msg"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/log"
+)
+
+func handleMsg(m interface{}, h interface{}) {
+	skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
+}
+
+// 大厅有进入大厅,离开大厅,加入房间,玩法列表,商城
+func init() {
+	handleMsg(&msg.EnterHall{}, enterHall)
+	handleMsg(&msg.LeaveHall{}, leaveHall)
+	handleMsg(&msg.MatchLudo{}, MatchLudo)
+}
+
+func enterHall(args []interface{}) {
+	m := args[0].(*msg.EnterHall)
+	a := args[1].(gate.Agent)
+
+	info, err_info := redismgr.GetUserInfoFromRedis(m.UserId)
+	if err_info != nil {
+		a.WriteMsg(&msg.ResEnterHall{
+			Success: false,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 101,
+				ErrorMsg:  "not find user",
+			},
+		})
+		return
+	}
+	//如果玩家在某个游戏里,就进行重连
+	if info.RoomId != 0 {
+		//进行重连逻辑
+	} else {
+		a.WriteMsg(&msg.ResEnterHall{
+			Success: true,
+			ErrMsg:  nil,
+		})
+	}
+	log.Debug("enterHallHandler, msg: %v, agent: %v", m, a)
+}
+
+func leaveHall(args []interface{}) {
+	m := args[0].(*msg.LeaveHall)
+	a := args[1].(gate.Agent)
+	log.Debug("leaveHallHandler, msg: %v, agent: %v", m, a)
+}
+
+// ludo匹配
+func MatchLudo(args []interface{}) {
+	m := args[0].(*msg.MatchLudo)
+	a := args[1].(gate.Agent)
+	events.EventChan <- events.Event{
+		Type:  events.EventLudoMatch,
+		Data:  m,
+		Agent: a,
+	}
+	log.Debug("MatchLudo, msg: %v, agent: %v", m, a)
+}

+ 23 - 0
src/server/hall/internal/module.go

@@ -0,0 +1,23 @@
+package internal
+
+import (
+	"github.com/name5566/leaf/module"
+	"server/base"
+)
+
+var (
+	skeleton = base.NewSkeleton()
+	ChanRPC  = skeleton.ChanRPCServer
+)
+
+type Module struct {
+	*module.Skeleton
+}
+
+func (m *Module) OnInit() {
+	m.Skeleton = skeleton
+}
+
+func (m *Module) OnDestroy() {
+
+}

+ 10 - 0
src/server/login/external.go

@@ -0,0 +1,10 @@
+package login
+
+import (
+	"server/login/internal"
+)
+
+var (
+	Module  = new(internal.Module)
+	ChanRPC = internal.ChanRPC
+)

+ 74 - 0
src/server/login/internal/handler.go

@@ -0,0 +1,74 @@
+package internal
+
+import (
+	"reflect"
+	redismgr "server/db/redis"
+	"server/game"
+	"server/msg"
+
+	"github.com/name5566/leaf/gate"
+	"github.com/name5566/leaf/log"
+)
+
+func handleMsg(m interface{}, h interface{}) {
+	skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
+}
+
+func init() {
+	handleMsg(&msg.ReqLogin{}, loginHandler)
+	handleMsg(&msg.ReqRegister{}, registerHandler)
+}
+
+func registerHandler(args []interface{}) {
+	// agent := args[1].(gate.Agent)
+	m := args[0].(*msg.ReqRegister)
+	// 消息的发送者
+	a := args[1].(gate.Agent)
+
+	userId, _ := redismgr.GetUserIDFromRedisByAP(m.Account, m.Password)
+
+	if userId != "" {
+		a.WriteMsg(&msg.ResRegister{
+			Success: false,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 101,
+				ErrorMsg:  " Account is exist!",
+			},
+		})
+		return
+	}
+
+	userId, Success := redismgr.SetUserIDFromRedisByAP(m.Account, m.Password)
+	if Success == false {
+		a.WriteMsg(&msg.ResRegister{
+			Success: false,
+			ErrMsg: &msg.MsgError{
+				ErrorCode: 102,
+				ErrorMsg:  "server registerHandler error",
+			},
+		})
+		return
+	}
+
+	redismgr.SaveUserInfoToRedis(&msg.UserInfo{
+		UserId: userId,
+		MCoin:  0,
+		MHead:  m.MHead,
+		Name:   m.NikeName,
+	})
+
+	a.WriteMsg(&msg.ResRegister{
+		Success: true,
+		ErrMsg:  nil,
+	})
+
+}
+func loginHandler(args []interface{}) {
+	// agent := args[1].(gate.Agent)
+	m := args[0].(*msg.ResLogin)
+	// 消息的发送者
+	a := args[1].(gate.Agent)
+
+	log.Debug("loginHandler: %v", args)
+	game.ChanRPC.Go("handleAuth", m, a)
+}

+ 23 - 0
src/server/login/internal/module.go

@@ -0,0 +1,23 @@
+package internal
+
+import (
+	"github.com/name5566/leaf/module"
+	"server/base"
+)
+
+var (
+	skeleton = base.NewSkeleton()
+	ChanRPC  = skeleton.ChanRPCServer
+)
+
+type Module struct {
+	*module.Skeleton
+}
+
+func (m *Module) OnInit() {
+	m.Skeleton = skeleton
+}
+
+func (m *Module) OnDestroy() {
+
+}

+ 29 - 0
src/server/main.go

@@ -0,0 +1,29 @@
+package main
+
+import (
+	"server/conf"
+	"server/game"
+	"server/gate"
+	"server/hall"
+	"server/login"
+
+	"github.com/name5566/leaf"
+	lconf "github.com/name5566/leaf/conf"
+)
+
+func setupConfig() {
+	lconf.LogLevel = conf.Server.LogLevel
+	lconf.LogPath = conf.Server.LogPath
+	lconf.LogFlag = conf.LogFlag
+	lconf.ConsolePort = conf.Server.ConsolePort
+	lconf.ProfilePath = conf.Server.ProfilePath
+}
+func main() {
+	setupConfig()
+	leaf.Run(
+		gate.Module,
+		login.Module,
+		game.Module,
+		hall.Module,
+	)
+}

+ 1 - 0
src/server/models/game.model.go

@@ -0,0 +1 @@
+package models

+ 56 - 0
src/server/models/user.model.go

@@ -0,0 +1,56 @@
+package models
+
+import (
+	"server/conf"
+	mongodbmgr "server/db/mongodb"
+
+	// redismgr "server/db/redis"
+	"time"
+
+	"gopkg.in/mgo.v2/bson"
+)
+
+// 定义用户集合操作
+var userCollection = mongodbmgr.NewCollection(conf.MongoDBName, "users")
+
+// 用户模型
+type User struct {
+	UserID    int64     `bson:"user_id"`
+	Name      string    `bson:"name"`
+	Password  string    `bson:"password"`
+	LastLogin time.Time `bson:"last_login"`
+}
+
+// func AddGameToList(userId string) {
+// 	// 推入 Redis 列表
+// 	err := redismgr.ListPush("user:"+userId+":games", "game1")
+// 	if err != nil {
+// 		log.Println("Error pushing to Redis:", err)
+// 	}
+// }
+
+// 获取最近三天有登录的用户,支持分页
+func GetThreeDayUsersWithPagination(skip, limit int) ([]User, error) {
+	var users []User
+	threeDaysAgo := time.Now().AddDate(0, 0, -3)
+	err := userCollection.FindMany(
+		bson.M{"last_login": bson.M{"$gte": threeDaysAgo}},
+		skip,
+		limit,
+		&users,
+	)
+	if err != nil {
+		return nil, err
+	}
+	return users, nil
+}
+
+// 根据用户ID获取用户信息
+func GetUserByID(userID int64) (*User, error) {
+	var user User
+	err := userCollection.FindOne(bson.M{"user_id": userID}, &user)
+	if err != nil {
+		return nil, err
+	}
+	return &user, nil
+}

+ 2208 - 0
src/server/msg/common.pb.go

@@ -0,0 +1,2208 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.36.6
+// 	protoc        v3.21.5
+// source: common.proto
+
+package msg
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+	unsafe "unsafe"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type RoleType int32
+
+const (
+	RoleType_ROLE_TYPE_UNKNOWN RoleType = 0
+	RoleType_RED               RoleType = 1 // 无操作
+	RoleType_BLUE              RoleType = 4
+	RoleType_YELLOW            RoleType = 3
+	RoleType_GREEN             RoleType = 2
+)
+
+// Enum value maps for RoleType.
+var (
+	RoleType_name = map[int32]string{
+		0: "ROLE_TYPE_UNKNOWN",
+		1: "RED",
+		4: "BLUE",
+		3: "YELLOW",
+		2: "GREEN",
+	}
+	RoleType_value = map[string]int32{
+		"ROLE_TYPE_UNKNOWN": 0,
+		"RED":               1,
+		"BLUE":              4,
+		"YELLOW":            3,
+		"GREEN":             2,
+	}
+)
+
+func (x RoleType) Enum() *RoleType {
+	p := new(RoleType)
+	*p = x
+	return p
+}
+
+func (x RoleType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RoleType) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[0].Descriptor()
+}
+
+func (RoleType) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[0]
+}
+
+func (x RoleType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RoleType.Descriptor instead.
+func (RoleType) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{0}
+}
+
+type OptType int32
+
+const (
+	OptType_OPT_TYPE_UNKNOWN OptType = 0
+	OptType_ZHI_SHAI_ZI      OptType = 1
+	OptType_SELECT_ROLE      OptType = 2
+)
+
+// Enum value maps for OptType.
+var (
+	OptType_name = map[int32]string{
+		0: "OPT_TYPE_UNKNOWN",
+		1: "ZHI_SHAI_ZI",
+		2: "SELECT_ROLE",
+	}
+	OptType_value = map[string]int32{
+		"OPT_TYPE_UNKNOWN": 0,
+		"ZHI_SHAI_ZI":      1,
+		"SELECT_ROLE":      2,
+	}
+)
+
+func (x OptType) Enum() *OptType {
+	p := new(OptType)
+	*p = x
+	return p
+}
+
+func (x OptType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (OptType) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[1].Descriptor()
+}
+
+func (OptType) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[1]
+}
+
+func (x OptType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use OptType.Descriptor instead.
+func (OptType) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{1}
+}
+
+type RoomType int32
+
+const (
+	RoomType_ROOM_TYPE_UNKNOWN RoomType = 0
+	RoomType_SHUANG_REN        RoomType = 1
+	RoomType_SIREN_REN         RoomType = 2
+)
+
+// Enum value maps for RoomType.
+var (
+	RoomType_name = map[int32]string{
+		0: "ROOM_TYPE_UNKNOWN",
+		1: "SHUANG_REN",
+		2: "SIREN_REN",
+	}
+	RoomType_value = map[string]int32{
+		"ROOM_TYPE_UNKNOWN": 0,
+		"SHUANG_REN":        1,
+		"SIREN_REN":         2,
+	}
+)
+
+func (x RoomType) Enum() *RoomType {
+	p := new(RoomType)
+	*p = x
+	return p
+}
+
+func (x RoomType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RoomType) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[2].Descriptor()
+}
+
+func (RoomType) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[2]
+}
+
+func (x RoomType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RoomType.Descriptor instead.
+func (RoomType) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{2}
+}
+
+type RoomMode int32
+
+const (
+	RoomMode_ROOM_MODE_UNKNOWN RoomMode = 0
+	RoomMode_REN_JI            RoomMode = 1
+	RoomMode_WAN_JIA           RoomMode = 2
+)
+
+// Enum value maps for RoomMode.
+var (
+	RoomMode_name = map[int32]string{
+		0: "ROOM_MODE_UNKNOWN",
+		1: "REN_JI",
+		2: "WAN_JIA",
+	}
+	RoomMode_value = map[string]int32{
+		"ROOM_MODE_UNKNOWN": 0,
+		"REN_JI":            1,
+		"WAN_JIA":           2,
+	}
+)
+
+func (x RoomMode) Enum() *RoomMode {
+	p := new(RoomMode)
+	*p = x
+	return p
+}
+
+func (x RoomMode) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RoomMode) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[3].Descriptor()
+}
+
+func (RoomMode) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[3]
+}
+
+func (x RoomMode) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RoomMode.Descriptor instead.
+func (RoomMode) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{3}
+}
+
+type RoadType int32
+
+const (
+	RoadType_ROAD_TYPE_UNKNOWN RoadType = 0
+	RoadType_HOME              RoadType = 1
+)
+
+// Enum value maps for RoadType.
+var (
+	RoadType_name = map[int32]string{
+		0: "ROAD_TYPE_UNKNOWN",
+		1: "HOME",
+	}
+	RoadType_value = map[string]int32{
+		"ROAD_TYPE_UNKNOWN": 0,
+		"HOME":              1,
+	}
+)
+
+func (x RoadType) Enum() *RoadType {
+	p := new(RoadType)
+	*p = x
+	return p
+}
+
+func (x RoadType) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RoadType) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[4].Descriptor()
+}
+
+func (RoadType) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[4]
+}
+
+func (x RoadType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RoadType.Descriptor instead.
+func (RoadType) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{4}
+}
+
+type PlayerStatus int32
+
+const (
+	PlayerStatus_PLAYER_STATUS_UNKNOWN PlayerStatus = 0
+	PlayerStatus_SZ_ANIMATION          PlayerStatus = 1
+	PlayerStatus_COLOR_FINISH          PlayerStatus = 2
+	PlayerStatus_COLOR_KICK            PlayerStatus = 3
+)
+
+// Enum value maps for PlayerStatus.
+var (
+	PlayerStatus_name = map[int32]string{
+		0: "PLAYER_STATUS_UNKNOWN",
+		1: "SZ_ANIMATION",
+		2: "COLOR_FINISH",
+		3: "COLOR_KICK",
+	}
+	PlayerStatus_value = map[string]int32{
+		"PLAYER_STATUS_UNKNOWN": 0,
+		"SZ_ANIMATION":          1,
+		"COLOR_FINISH":          2,
+		"COLOR_KICK":            3,
+	}
+)
+
+func (x PlayerStatus) Enum() *PlayerStatus {
+	p := new(PlayerStatus)
+	*p = x
+	return p
+}
+
+func (x PlayerStatus) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (PlayerStatus) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[5].Descriptor()
+}
+
+func (PlayerStatus) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[5]
+}
+
+func (x PlayerStatus) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use PlayerStatus.Descriptor instead.
+func (PlayerStatus) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{5}
+}
+
+type RoomStatus int32
+
+const (
+	RoomStatus_AWAIT RoomStatus = 0
+	RoomStatus_START RoomStatus = 1
+	RoomStatus_END   RoomStatus = 2
+)
+
+// Enum value maps for RoomStatus.
+var (
+	RoomStatus_name = map[int32]string{
+		0: "AWAIT",
+		1: "START",
+		2: "END",
+	}
+	RoomStatus_value = map[string]int32{
+		"AWAIT": 0,
+		"START": 1,
+		"END":   2,
+	}
+)
+
+func (x RoomStatus) Enum() *RoomStatus {
+	p := new(RoomStatus)
+	*p = x
+	return p
+}
+
+func (x RoomStatus) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (RoomStatus) Descriptor() protoreflect.EnumDescriptor {
+	return file_common_proto_enumTypes[6].Descriptor()
+}
+
+func (RoomStatus) Type() protoreflect.EnumType {
+	return &file_common_proto_enumTypes[6]
+}
+
+func (x RoomStatus) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use RoomStatus.Descriptor instead.
+func (RoomStatus) EnumDescriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{6}
+}
+
+type Round struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	MColor        RoleType               `protobuf:"varint,1,opt,name=m_color,json=mColor,proto3,enum=RoleType" json:"m_color,omitempty"`
+	MRoad         string                 `protobuf:"bytes,2,opt,name=m_road,json=mRoad,proto3" json:"m_road,omitempty"`
+	Opt           OptType                `protobuf:"varint,3,opt,name=opt,proto3,enum=OptType" json:"opt,omitempty"`
+	SzNumber      int32                  `protobuf:"varint,4,opt,name=szNumber,proto3" json:"szNumber,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *Round) Reset() {
+	*x = Round{}
+	mi := &file_common_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *Round) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Round) ProtoMessage() {}
+
+func (x *Round) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Round.ProtoReflect.Descriptor instead.
+func (*Round) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Round) GetMColor() RoleType {
+	if x != nil {
+		return x.MColor
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *Round) GetMRoad() string {
+	if x != nil {
+		return x.MRoad
+	}
+	return ""
+}
+
+func (x *Round) GetOpt() OptType {
+	if x != nil {
+		return x.Opt
+	}
+	return OptType_OPT_TYPE_UNKNOWN
+}
+
+func (x *Round) GetSzNumber() int32 {
+	if x != nil {
+		return x.SzNumber
+	}
+	return 0
+}
+
+type RoomInfo struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Roles         []*RoleData            `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"`
+	Colors        []*ColorData           `protobuf:"bytes,2,rep,name=colors,proto3" json:"colors,omitempty"`
+	RoomType      RoomType               `protobuf:"varint,3,opt,name=room_type,json=roomType,proto3,enum=RoomType" json:"room_type,omitempty"`
+	RoomMode      RoomMode               `protobuf:"varint,4,opt,name=room_mode,json=roomMode,proto3,enum=RoomMode" json:"room_mode,omitempty"`
+	CurRoundColor RoleType               `protobuf:"varint,5,opt,name=cur_round_color,json=curRoundColor,proto3,enum=RoleType" json:"cur_round_color,omitempty"`
+	Rounds        []*Round               `protobuf:"bytes,6,rep,name=rounds,proto3" json:"rounds,omitempty"`
+	FinishColors  []*ColorData           `protobuf:"bytes,7,rep,name=finish_colors,json=finishColors,proto3" json:"finish_colors,omitempty"`
+	KictColors    []*ColorData           `protobuf:"bytes,8,rep,name=kict_colors,json=kictColors,proto3" json:"kict_colors,omitempty"`
+	Id            int32                  `protobuf:"varint,9,opt,name=id,proto3" json:"id,omitempty"`
+	RoomLevel     int32                  `protobuf:"varint,10,opt,name=room_level,json=roomLevel,proto3" json:"room_level,omitempty"` //房间的每个等级都对应着不同的奖励和消耗
+	RoomStatus    RoomStatus             `protobuf:"varint,11,opt,name=room_status,json=roomStatus,proto3,enum=RoomStatus" json:"room_status,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *RoomInfo) Reset() {
+	*x = RoomInfo{}
+	mi := &file_common_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *RoomInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RoomInfo) ProtoMessage() {}
+
+func (x *RoomInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RoomInfo.ProtoReflect.Descriptor instead.
+func (*RoomInfo) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *RoomInfo) GetRoles() []*RoleData {
+	if x != nil {
+		return x.Roles
+	}
+	return nil
+}
+
+func (x *RoomInfo) GetColors() []*ColorData {
+	if x != nil {
+		return x.Colors
+	}
+	return nil
+}
+
+func (x *RoomInfo) GetRoomType() RoomType {
+	if x != nil {
+		return x.RoomType
+	}
+	return RoomType_ROOM_TYPE_UNKNOWN
+}
+
+func (x *RoomInfo) GetRoomMode() RoomMode {
+	if x != nil {
+		return x.RoomMode
+	}
+	return RoomMode_ROOM_MODE_UNKNOWN
+}
+
+func (x *RoomInfo) GetCurRoundColor() RoleType {
+	if x != nil {
+		return x.CurRoundColor
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *RoomInfo) GetRounds() []*Round {
+	if x != nil {
+		return x.Rounds
+	}
+	return nil
+}
+
+func (x *RoomInfo) GetFinishColors() []*ColorData {
+	if x != nil {
+		return x.FinishColors
+	}
+	return nil
+}
+
+func (x *RoomInfo) GetKictColors() []*ColorData {
+	if x != nil {
+		return x.KictColors
+	}
+	return nil
+}
+
+func (x *RoomInfo) GetId() int32 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *RoomInfo) GetRoomLevel() int32 {
+	if x != nil {
+		return x.RoomLevel
+	}
+	return 0
+}
+
+func (x *RoomInfo) GetRoomStatus() RoomStatus {
+	if x != nil {
+		return x.RoomStatus
+	}
+	return RoomStatus_AWAIT
+}
+
+type RoleData struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	MColor        RoleType               `protobuf:"varint,1,opt,name=m_color,json=mColor,proto3,enum=RoleType" json:"m_color,omitempty"`
+	MSeat         int32                  `protobuf:"varint,2,opt,name=m_seat,json=mSeat,proto3" json:"m_seat,omitempty"`
+	MId           string                 `protobuf:"bytes,3,opt,name=m_id,json=mId,proto3" json:"m_id,omitempty"`
+	MCurRoad      RoadType               `protobuf:"varint,4,opt,name=m_cur_road,json=mCurRoad,proto3,enum=RoadType" json:"m_cur_road,omitempty"`
+	Step          int32                  `protobuf:"varint,5,opt,name=step,proto3" json:"step,omitempty"`
+	OldSetp       int32                  `protobuf:"varint,6,opt,name=old_setp,json=oldSetp,proto3" json:"old_setp,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *RoleData) Reset() {
+	*x = RoleData{}
+	mi := &file_common_proto_msgTypes[2]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *RoleData) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RoleData) ProtoMessage() {}
+
+func (x *RoleData) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[2]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RoleData.ProtoReflect.Descriptor instead.
+func (*RoleData) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *RoleData) GetMColor() RoleType {
+	if x != nil {
+		return x.MColor
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *RoleData) GetMSeat() int32 {
+	if x != nil {
+		return x.MSeat
+	}
+	return 0
+}
+
+func (x *RoleData) GetMId() string {
+	if x != nil {
+		return x.MId
+	}
+	return ""
+}
+
+func (x *RoleData) GetMCurRoad() RoadType {
+	if x != nil {
+		return x.MCurRoad
+	}
+	return RoadType_ROAD_TYPE_UNKNOWN
+}
+
+func (x *RoleData) GetStep() int32 {
+	if x != nil {
+		return x.Step
+	}
+	return 0
+}
+
+func (x *RoleData) GetOldSetp() int32 {
+	if x != nil {
+		return x.OldSetp
+	}
+	return 0
+}
+
+type ColorData struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	MId           string                 `protobuf:"bytes,1,opt,name=m_id,json=mId,proto3" json:"m_id,omitempty"`
+	MColor        RoleType               `protobuf:"varint,2,opt,name=m_color,json=mColor,proto3,enum=RoleType" json:"m_color,omitempty"`
+	IsKick        bool                   `protobuf:"varint,3,opt,name=is_kick,json=isKick,proto3" json:"is_kick,omitempty"`
+	IsFinish      bool                   `protobuf:"varint,4,opt,name=is_finish,json=isFinish,proto3" json:"is_finish,omitempty"`
+	TimeOutNum    int32                  `protobuf:"varint,5,opt,name=time_out_num,json=timeOutNum,proto3" json:"time_out_num,omitempty"`
+	MName         string                 `protobuf:"bytes,6,opt,name=m_name,json=mName,proto3" json:"m_name,omitempty"`
+	MHead         string                 `protobuf:"bytes,7,opt,name=m_head,json=mHead,proto3" json:"m_head,omitempty"`
+	RankNum       int32                  `protobuf:"varint,8,opt,name=rank_num,json=rankNum,proto3" json:"rank_num,omitempty"`
+	MCoin         int32                  `protobuf:"varint,9,opt,name=m_coin,json=mCoin,proto3" json:"m_coin,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ColorData) Reset() {
+	*x = ColorData{}
+	mi := &file_common_proto_msgTypes[3]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ColorData) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ColorData) ProtoMessage() {}
+
+func (x *ColorData) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[3]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ColorData.ProtoReflect.Descriptor instead.
+func (*ColorData) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ColorData) GetMId() string {
+	if x != nil {
+		return x.MId
+	}
+	return ""
+}
+
+func (x *ColorData) GetMColor() RoleType {
+	if x != nil {
+		return x.MColor
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *ColorData) GetIsKick() bool {
+	if x != nil {
+		return x.IsKick
+	}
+	return false
+}
+
+func (x *ColorData) GetIsFinish() bool {
+	if x != nil {
+		return x.IsFinish
+	}
+	return false
+}
+
+func (x *ColorData) GetTimeOutNum() int32 {
+	if x != nil {
+		return x.TimeOutNum
+	}
+	return 0
+}
+
+func (x *ColorData) GetMName() string {
+	if x != nil {
+		return x.MName
+	}
+	return ""
+}
+
+func (x *ColorData) GetMHead() string {
+	if x != nil {
+		return x.MHead
+	}
+	return ""
+}
+
+func (x *ColorData) GetRankNum() int32 {
+	if x != nil {
+		return x.RankNum
+	}
+	return 0
+}
+
+func (x *ColorData) GetMCoin() int32 {
+	if x != nil {
+		return x.MCoin
+	}
+	return 0
+}
+
+type MoveStepData struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	MId           string                 `protobuf:"bytes,1,opt,name=m_id,json=mId,proto3" json:"m_id,omitempty"`
+	Step          int32                  `protobuf:"varint,2,opt,name=step,proto3" json:"step,omitempty"`
+	OldSetp       int32                  `protobuf:"varint,3,opt,name=old_setp,json=oldSetp,proto3" json:"old_setp,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *MoveStepData) Reset() {
+	*x = MoveStepData{}
+	mi := &file_common_proto_msgTypes[4]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *MoveStepData) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MoveStepData) ProtoMessage() {}
+
+func (x *MoveStepData) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[4]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MoveStepData.ProtoReflect.Descriptor instead.
+func (*MoveStepData) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *MoveStepData) GetMId() string {
+	if x != nil {
+		return x.MId
+	}
+	return ""
+}
+
+func (x *MoveStepData) GetStep() int32 {
+	if x != nil {
+		return x.Step
+	}
+	return 0
+}
+
+func (x *MoveStepData) GetOldSetp() int32 {
+	if x != nil {
+		return x.OldSetp
+	}
+	return 0
+}
+
+type SendColorSz struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *SendColorSz) Reset() {
+	*x = SendColorSz{}
+	mi := &file_common_proto_msgTypes[5]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *SendColorSz) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SendColorSz) ProtoMessage() {}
+
+func (x *SendColorSz) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[5]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SendColorSz.ProtoReflect.Descriptor instead.
+func (*SendColorSz) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *SendColorSz) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+type SendRoleMove struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	RoleId        string                 `protobuf:"bytes,2,opt,name=roleId,proto3" json:"roleId,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *SendRoleMove) Reset() {
+	*x = SendRoleMove{}
+	mi := &file_common_proto_msgTypes[6]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *SendRoleMove) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SendRoleMove) ProtoMessage() {}
+
+func (x *SendRoleMove) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[6]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SendRoleMove.ProtoReflect.Descriptor instead.
+func (*SendRoleMove) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *SendRoleMove) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *SendRoleMove) GetRoleId() string {
+	if x != nil {
+		return x.RoleId
+	}
+	return ""
+}
+
+type NotifyPlayerSzNumber struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	SzNumber      int32                  `protobuf:"varint,2,opt,name=szNumber,proto3" json:"szNumber,omitempty"`         //
+	ErrMsg        *MsgError              `protobuf:"bytes,3,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *NotifyPlayerSzNumber) Reset() {
+	*x = NotifyPlayerSzNumber{}
+	mi := &file_common_proto_msgTypes[7]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *NotifyPlayerSzNumber) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NotifyPlayerSzNumber) ProtoMessage() {}
+
+func (x *NotifyPlayerSzNumber) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[7]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NotifyPlayerSzNumber.ProtoReflect.Descriptor instead.
+func (*NotifyPlayerSzNumber) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *NotifyPlayerSzNumber) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *NotifyPlayerSzNumber) GetSzNumber() int32 {
+	if x != nil {
+		return x.SzNumber
+	}
+	return 0
+}
+
+func (x *NotifyPlayerSzNumber) GetErrMsg() *MsgError {
+	if x != nil {
+		return x.ErrMsg
+	}
+	return nil
+}
+
+type NotifyPlayerMove struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	Step          *MoveStepData          `protobuf:"bytes,2,opt,name=step,proto3" json:"step,omitempty"`                  //
+	Kick          []*RoleData            `protobuf:"bytes,3,rep,name=kick,proto3" json:"kick,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *NotifyPlayerMove) Reset() {
+	*x = NotifyPlayerMove{}
+	mi := &file_common_proto_msgTypes[8]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *NotifyPlayerMove) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NotifyPlayerMove) ProtoMessage() {}
+
+func (x *NotifyPlayerMove) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[8]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NotifyPlayerMove.ProtoReflect.Descriptor instead.
+func (*NotifyPlayerMove) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *NotifyPlayerMove) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *NotifyPlayerMove) GetStep() *MoveStepData {
+	if x != nil {
+		return x.Step
+	}
+	return nil
+}
+
+func (x *NotifyPlayerMove) GetKick() []*RoleData {
+	if x != nil {
+		return x.Kick
+	}
+	return nil
+}
+
+type NotifyPlayerOpt struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	Opt           OptType                `protobuf:"varint,2,opt,name=opt,proto3,enum=OptType" json:"opt,omitempty"`      //
+	CanMoveRoles  []*RoleData            `protobuf:"bytes,3,rep,name=canMoveRoles,proto3" json:"canMoveRoles,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *NotifyPlayerOpt) Reset() {
+	*x = NotifyPlayerOpt{}
+	mi := &file_common_proto_msgTypes[9]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *NotifyPlayerOpt) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NotifyPlayerOpt) ProtoMessage() {}
+
+func (x *NotifyPlayerOpt) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[9]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NotifyPlayerOpt.ProtoReflect.Descriptor instead.
+func (*NotifyPlayerOpt) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *NotifyPlayerOpt) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *NotifyPlayerOpt) GetOpt() OptType {
+	if x != nil {
+		return x.Opt
+	}
+	return OptType_OPT_TYPE_UNKNOWN
+}
+
+func (x *NotifyPlayerOpt) GetCanMoveRoles() []*RoleData {
+	if x != nil {
+		return x.CanMoveRoles
+	}
+	return nil
+}
+
+type NotifyPlayerStatus struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	Status        PlayerStatus           `protobuf:"varint,2,opt,name=status,proto3,enum=PlayerStatus" json:"status,omitempty"`
+	Colors        []*ColorData           `protobuf:"bytes,3,rep,name=colors,proto3" json:"colors,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *NotifyPlayerStatus) Reset() {
+	*x = NotifyPlayerStatus{}
+	mi := &file_common_proto_msgTypes[10]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *NotifyPlayerStatus) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NotifyPlayerStatus) ProtoMessage() {}
+
+func (x *NotifyPlayerStatus) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[10]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NotifyPlayerStatus.ProtoReflect.Descriptor instead.
+func (*NotifyPlayerStatus) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *NotifyPlayerStatus) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *NotifyPlayerStatus) GetStatus() PlayerStatus {
+	if x != nil {
+		return x.Status
+	}
+	return PlayerStatus_PLAYER_STATUS_UNKNOWN
+}
+
+func (x *NotifyPlayerStatus) GetColors() []*ColorData {
+	if x != nil {
+		return x.Colors
+	}
+	return nil
+}
+
+type NotifySettlement struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Color         RoleType               `protobuf:"varint,1,opt,name=color,proto3,enum=RoleType" json:"color,omitempty"` //
+	FinishColors  []*ColorData           `protobuf:"bytes,2,rep,name=finish_colors,json=finishColors,proto3" json:"finish_colors,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *NotifySettlement) Reset() {
+	*x = NotifySettlement{}
+	mi := &file_common_proto_msgTypes[11]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *NotifySettlement) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NotifySettlement) ProtoMessage() {}
+
+func (x *NotifySettlement) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[11]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NotifySettlement.ProtoReflect.Descriptor instead.
+func (*NotifySettlement) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *NotifySettlement) GetColor() RoleType {
+	if x != nil {
+		return x.Color
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+func (x *NotifySettlement) GetFinishColors() []*ColorData {
+	if x != nil {
+		return x.FinishColors
+	}
+	return nil
+}
+
+// 用户信息
+type UserInfo struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=UserId,proto3" json:"UserId,omitempty"`
+	MHead         string                 `protobuf:"bytes,2,opt,name=m_head,json=mHead,proto3" json:"m_head,omitempty"`
+	MCoin         int32                  `protobuf:"varint,3,opt,name=m_coin,json=mCoin,proto3" json:"m_coin,omitempty"`
+	Name          string                 `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
+	RoomId        int32                  `protobuf:"varint,5,opt,name=room_id,json=roomId,proto3" json:"room_id,omitempty"` //如果用户在某一个房间玩,他的id就一直存在,
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *UserInfo) Reset() {
+	*x = UserInfo{}
+	mi := &file_common_proto_msgTypes[12]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *UserInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UserInfo) ProtoMessage() {}
+
+func (x *UserInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[12]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UserInfo.ProtoReflect.Descriptor instead.
+func (*UserInfo) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *UserInfo) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+func (x *UserInfo) GetMHead() string {
+	if x != nil {
+		return x.MHead
+	}
+	return ""
+}
+
+func (x *UserInfo) GetMCoin() int32 {
+	if x != nil {
+		return x.MCoin
+	}
+	return 0
+}
+
+func (x *UserInfo) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *UserInfo) GetRoomId() int32 {
+	if x != nil {
+		return x.RoomId
+	}
+	return 0
+}
+
+// 登录 请求
+type ReqLogin struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Account       string                 `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
+	Password      string                 `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ReqLogin) Reset() {
+	*x = ReqLogin{}
+	mi := &file_common_proto_msgTypes[13]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ReqLogin) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReqLogin) ProtoMessage() {}
+
+func (x *ReqLogin) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[13]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReqLogin.ProtoReflect.Descriptor instead.
+func (*ReqLogin) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *ReqLogin) GetAccount() string {
+	if x != nil {
+		return x.Account
+	}
+	return ""
+}
+
+func (x *ReqLogin) GetPassword() string {
+	if x != nil {
+		return x.Password
+	}
+	return ""
+}
+
+// 登录 响应
+type ResLogin struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=userId,proto3" json:"userId,omitempty"`
+	NikeName      string                 `protobuf:"bytes,2,opt,name=nikeName,proto3" json:"nikeName,omitempty"`
+	UserInfo      *UserInfo              `protobuf:"bytes,3,opt,name=userInfo,proto3" json:"userInfo,omitempty"`
+	ErrMsg        *MsgError              `protobuf:"bytes,4,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ResLogin) Reset() {
+	*x = ResLogin{}
+	mi := &file_common_proto_msgTypes[14]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ResLogin) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResLogin) ProtoMessage() {}
+
+func (x *ResLogin) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[14]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResLogin.ProtoReflect.Descriptor instead.
+func (*ResLogin) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *ResLogin) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+func (x *ResLogin) GetNikeName() string {
+	if x != nil {
+		return x.NikeName
+	}
+	return ""
+}
+
+func (x *ResLogin) GetUserInfo() *UserInfo {
+	if x != nil {
+		return x.UserInfo
+	}
+	return nil
+}
+
+func (x *ResLogin) GetErrMsg() *MsgError {
+	if x != nil {
+		return x.ErrMsg
+	}
+	return nil
+}
+
+// 注册 请求
+type ReqRegister struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	NikeName      string                 `protobuf:"bytes,1,opt,name=nikeName,proto3" json:"nikeName,omitempty"`
+	Account       string                 `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"`
+	Password      string                 `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"`
+	MHead         string                 `protobuf:"bytes,4,opt,name=m_head,json=mHead,proto3" json:"m_head,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ReqRegister) Reset() {
+	*x = ReqRegister{}
+	mi := &file_common_proto_msgTypes[15]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ReqRegister) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReqRegister) ProtoMessage() {}
+
+func (x *ReqRegister) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[15]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReqRegister.ProtoReflect.Descriptor instead.
+func (*ReqRegister) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *ReqRegister) GetNikeName() string {
+	if x != nil {
+		return x.NikeName
+	}
+	return ""
+}
+
+func (x *ReqRegister) GetAccount() string {
+	if x != nil {
+		return x.Account
+	}
+	return ""
+}
+
+func (x *ReqRegister) GetPassword() string {
+	if x != nil {
+		return x.Password
+	}
+	return ""
+}
+
+func (x *ReqRegister) GetMHead() string {
+	if x != nil {
+		return x.MHead
+	}
+	return ""
+}
+
+// 注册 响应
+type ResRegister struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Success       bool                   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
+	ErrMsg        *MsgError              `protobuf:"bytes,2,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ResRegister) Reset() {
+	*x = ResRegister{}
+	mi := &file_common_proto_msgTypes[16]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ResRegister) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResRegister) ProtoMessage() {}
+
+func (x *ResRegister) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[16]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResRegister.ProtoReflect.Descriptor instead.
+func (*ResRegister) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{16}
+}
+
+func (x *ResRegister) GetSuccess() bool {
+	if x != nil {
+		return x.Success
+	}
+	return false
+}
+
+func (x *ResRegister) GetErrMsg() *MsgError {
+	if x != nil {
+		return x.ErrMsg
+	}
+	return nil
+}
+
+// 进入大厅
+type EnterHall struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=userId,proto3" json:"userId,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *EnterHall) Reset() {
+	*x = EnterHall{}
+	mi := &file_common_proto_msgTypes[17]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *EnterHall) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EnterHall) ProtoMessage() {}
+
+func (x *EnterHall) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[17]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use EnterHall.ProtoReflect.Descriptor instead.
+func (*EnterHall) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *EnterHall) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+// 进入大厅响应
+type ResEnterHall struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Success       bool                   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
+	ErrMsg        *MsgError              `protobuf:"bytes,2,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ResEnterHall) Reset() {
+	*x = ResEnterHall{}
+	mi := &file_common_proto_msgTypes[18]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ResEnterHall) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResEnterHall) ProtoMessage() {}
+
+func (x *ResEnterHall) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[18]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResEnterHall.ProtoReflect.Descriptor instead.
+func (*ResEnterHall) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *ResEnterHall) GetSuccess() bool {
+	if x != nil {
+		return x.Success
+	}
+	return false
+}
+
+func (x *ResEnterHall) GetErrMsg() *MsgError {
+	if x != nil {
+		return x.ErrMsg
+	}
+	return nil
+}
+
+// 离开大厅
+type LeaveHall struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=userId,proto3" json:"userId,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *LeaveHall) Reset() {
+	*x = LeaveHall{}
+	mi := &file_common_proto_msgTypes[19]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *LeaveHall) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LeaveHall) ProtoMessage() {}
+
+func (x *LeaveHall) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[19]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LeaveHall.ProtoReflect.Descriptor instead.
+func (*LeaveHall) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *LeaveHall) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+// 匹配ludo
+type MatchLudo struct {
+	state          protoimpl.MessageState `protogen:"open.v1"`
+	SelectRoomType RoomType               `protobuf:"varint,1,opt,name=select_room_type,json=selectRoomType,proto3,enum=RoomType" json:"select_room_type,omitempty"`
+	RoomLevel      int32                  `protobuf:"varint,2,opt,name=room_level,json=roomLevel,proto3" json:"room_level,omitempty"`
+	SelectColor    RoleType               `protobuf:"varint,3,opt,name=select_color,json=selectColor,proto3,enum=RoleType" json:"select_color,omitempty"`
+	unknownFields  protoimpl.UnknownFields
+	sizeCache      protoimpl.SizeCache
+}
+
+func (x *MatchLudo) Reset() {
+	*x = MatchLudo{}
+	mi := &file_common_proto_msgTypes[20]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *MatchLudo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MatchLudo) ProtoMessage() {}
+
+func (x *MatchLudo) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[20]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MatchLudo.ProtoReflect.Descriptor instead.
+func (*MatchLudo) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *MatchLudo) GetSelectRoomType() RoomType {
+	if x != nil {
+		return x.SelectRoomType
+	}
+	return RoomType_ROOM_TYPE_UNKNOWN
+}
+
+func (x *MatchLudo) GetRoomLevel() int32 {
+	if x != nil {
+		return x.RoomLevel
+	}
+	return 0
+}
+
+func (x *MatchLudo) GetSelectColor() RoleType {
+	if x != nil {
+		return x.SelectColor
+	}
+	return RoleType_ROLE_TYPE_UNKNOWN
+}
+
+// 匹配ludo 响应
+type ResMatchLudo struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Success       bool                   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
+	ErrMsg        *MsgError              `protobuf:"bytes,2,opt,name=err_msg,json=errMsg,proto3" json:"err_msg,omitempty"`
+	Room          *RoomInfo              `protobuf:"bytes,3,opt,name=room,proto3" json:"room,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ResMatchLudo) Reset() {
+	*x = ResMatchLudo{}
+	mi := &file_common_proto_msgTypes[21]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ResMatchLudo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResMatchLudo) ProtoMessage() {}
+
+func (x *ResMatchLudo) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[21]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResMatchLudo.ProtoReflect.Descriptor instead.
+func (*ResMatchLudo) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *ResMatchLudo) GetSuccess() bool {
+	if x != nil {
+		return x.Success
+	}
+	return false
+}
+
+func (x *ResMatchLudo) GetErrMsg() *MsgError {
+	if x != nil {
+		return x.ErrMsg
+	}
+	return nil
+}
+
+func (x *ResMatchLudo) GetRoom() *RoomInfo {
+	if x != nil {
+		return x.Room
+	}
+	return nil
+}
+
+// error
+type MsgError struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	ErrorCode     int32                  `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"`
+	ErrorMsg      string                 `protobuf:"bytes,2,opt,name=error_msg,json=errorMsg,proto3" json:"error_msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *MsgError) Reset() {
+	*x = MsgError{}
+	mi := &file_common_proto_msgTypes[22]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *MsgError) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MsgError) ProtoMessage() {}
+
+func (x *MsgError) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[22]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MsgError.ProtoReflect.Descriptor instead.
+func (*MsgError) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *MsgError) GetErrorCode() int32 {
+	if x != nil {
+		return x.ErrorCode
+	}
+	return 0
+}
+
+func (x *MsgError) GetErrorMsg() string {
+	if x != nil {
+		return x.ErrorMsg
+	}
+	return ""
+}
+
+type ResHeartBeat struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Msg           string                 `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ResHeartBeat) Reset() {
+	*x = ResHeartBeat{}
+	mi := &file_common_proto_msgTypes[23]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ResHeartBeat) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResHeartBeat) ProtoMessage() {}
+
+func (x *ResHeartBeat) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[23]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResHeartBeat.ProtoReflect.Descriptor instead.
+func (*ResHeartBeat) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *ResHeartBeat) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+type ReqHeartBeat struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Msg           string                 `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ReqHeartBeat) Reset() {
+	*x = ReqHeartBeat{}
+	mi := &file_common_proto_msgTypes[24]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ReqHeartBeat) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ReqHeartBeat) ProtoMessage() {}
+
+func (x *ReqHeartBeat) ProtoReflect() protoreflect.Message {
+	mi := &file_common_proto_msgTypes[24]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ReqHeartBeat.ProtoReflect.Descriptor instead.
+func (*ReqHeartBeat) Descriptor() ([]byte, []int) {
+	return file_common_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *ReqHeartBeat) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+var File_common_proto protoreflect.FileDescriptor
+
+const file_common_proto_rawDesc = "" +
+	"\n" +
+	"\fcommon.proto\"z\n" +
+	"\x05round\x12\"\n" +
+	"\am_color\x18\x01 \x01(\x0e2\t.roleTypeR\x06mColor\x12\x15\n" +
+	"\x06m_road\x18\x02 \x01(\tR\x05mRoad\x12\x1a\n" +
+	"\x03opt\x18\x03 \x01(\x0e2\b.OptTypeR\x03opt\x12\x1a\n" +
+	"\bszNumber\x18\x04 \x01(\x05R\bszNumber\"\xad\x03\n" +
+	"\bRoomInfo\x12\x1f\n" +
+	"\x05roles\x18\x01 \x03(\v2\t.RoleDataR\x05roles\x12\"\n" +
+	"\x06colors\x18\x02 \x03(\v2\n" +
+	".ColorDataR\x06colors\x12&\n" +
+	"\troom_type\x18\x03 \x01(\x0e2\t.roomTypeR\broomType\x12&\n" +
+	"\troom_mode\x18\x04 \x01(\x0e2\t.roomModeR\broomMode\x121\n" +
+	"\x0fcur_round_color\x18\x05 \x01(\x0e2\t.roleTypeR\rcurRoundColor\x12\x1e\n" +
+	"\x06rounds\x18\x06 \x03(\v2\x06.roundR\x06rounds\x12/\n" +
+	"\rfinish_colors\x18\a \x03(\v2\n" +
+	".ColorDataR\ffinishColors\x12+\n" +
+	"\vkict_colors\x18\b \x03(\v2\n" +
+	".ColorDataR\n" +
+	"kictColors\x12\x0e\n" +
+	"\x02id\x18\t \x01(\x05R\x02id\x12\x1d\n" +
+	"\n" +
+	"room_level\x18\n" +
+	" \x01(\x05R\troomLevel\x12,\n" +
+	"\vroom_status\x18\v \x01(\x0e2\v.roomStatusR\n" +
+	"roomStatus\"\xb0\x01\n" +
+	"\bRoleData\x12\"\n" +
+	"\am_color\x18\x01 \x01(\x0e2\t.roleTypeR\x06mColor\x12\x15\n" +
+	"\x06m_seat\x18\x02 \x01(\x05R\x05mSeat\x12\x11\n" +
+	"\x04m_id\x18\x03 \x01(\tR\x03mId\x12'\n" +
+	"\n" +
+	"m_cur_road\x18\x04 \x01(\x0e2\t.roadTypeR\bmCurRoad\x12\x12\n" +
+	"\x04step\x18\x05 \x01(\x05R\x04step\x12\x19\n" +
+	"\bold_setp\x18\x06 \x01(\x05R\aoldSetp\"\xfa\x01\n" +
+	"\tColorData\x12\x11\n" +
+	"\x04m_id\x18\x01 \x01(\tR\x03mId\x12\"\n" +
+	"\am_color\x18\x02 \x01(\x0e2\t.roleTypeR\x06mColor\x12\x17\n" +
+	"\ais_kick\x18\x03 \x01(\bR\x06isKick\x12\x1b\n" +
+	"\tis_finish\x18\x04 \x01(\bR\bisFinish\x12 \n" +
+	"\ftime_out_num\x18\x05 \x01(\x05R\n" +
+	"timeOutNum\x12\x15\n" +
+	"\x06m_name\x18\x06 \x01(\tR\x05mName\x12\x15\n" +
+	"\x06m_head\x18\a \x01(\tR\x05mHead\x12\x19\n" +
+	"\brank_num\x18\b \x01(\x05R\arankNum\x12\x15\n" +
+	"\x06m_coin\x18\t \x01(\x05R\x05mCoin\"P\n" +
+	"\fMoveStepData\x12\x11\n" +
+	"\x04m_id\x18\x01 \x01(\tR\x03mId\x12\x12\n" +
+	"\x04step\x18\x02 \x01(\x05R\x04step\x12\x19\n" +
+	"\bold_setp\x18\x03 \x01(\x05R\aoldSetp\".\n" +
+	"\vSendColorSz\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\"G\n" +
+	"\fSendRoleMove\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12\x16\n" +
+	"\x06roleId\x18\x02 \x01(\tR\x06roleId\"w\n" +
+	"\x14NotifyPlayerSzNumber\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12\x1a\n" +
+	"\bszNumber\x18\x02 \x01(\x05R\bszNumber\x12\"\n" +
+	"\aerr_msg\x18\x03 \x01(\v2\t.MsgErrorR\x06errMsg\"u\n" +
+	"\x10NotifyPlayerMove\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12!\n" +
+	"\x04step\x18\x02 \x01(\v2\r.MoveStepDataR\x04step\x12\x1d\n" +
+	"\x04kick\x18\x03 \x03(\v2\t.RoleDataR\x04kick\"}\n" +
+	"\x0fNotifyPlayerOpt\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12\x1a\n" +
+	"\x03opt\x18\x02 \x01(\x0e2\b.OptTypeR\x03opt\x12-\n" +
+	"\fcanMoveRoles\x18\x03 \x03(\v2\t.RoleDataR\fcanMoveRoles\"\x80\x01\n" +
+	"\x12NotifyPlayerStatus\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12%\n" +
+	"\x06status\x18\x02 \x01(\x0e2\r.playerStatusR\x06status\x12\"\n" +
+	"\x06colors\x18\x03 \x03(\v2\n" +
+	".ColorDataR\x06colors\"d\n" +
+	"\x10NotifySettlement\x12\x1f\n" +
+	"\x05color\x18\x01 \x01(\x0e2\t.roleTypeR\x05color\x12/\n" +
+	"\rfinish_colors\x18\x02 \x03(\v2\n" +
+	".ColorDataR\ffinishColors\"}\n" +
+	"\bUserInfo\x12\x16\n" +
+	"\x06UserId\x18\x01 \x01(\tR\x06UserId\x12\x15\n" +
+	"\x06m_head\x18\x02 \x01(\tR\x05mHead\x12\x15\n" +
+	"\x06m_coin\x18\x03 \x01(\x05R\x05mCoin\x12\x12\n" +
+	"\x04name\x18\x04 \x01(\tR\x04name\x12\x17\n" +
+	"\aroom_id\x18\x05 \x01(\x05R\x06roomId\"@\n" +
+	"\bReqLogin\x12\x18\n" +
+	"\aaccount\x18\x01 \x01(\tR\aaccount\x12\x1a\n" +
+	"\bpassword\x18\x02 \x01(\tR\bpassword\"\x89\x01\n" +
+	"\bResLogin\x12\x16\n" +
+	"\x06userId\x18\x01 \x01(\tR\x06userId\x12\x1a\n" +
+	"\bnikeName\x18\x02 \x01(\tR\bnikeName\x12%\n" +
+	"\buserInfo\x18\x03 \x01(\v2\t.UserInfoR\buserInfo\x12\"\n" +
+	"\aerr_msg\x18\x04 \x01(\v2\t.MsgErrorR\x06errMsg\"v\n" +
+	"\vReqRegister\x12\x1a\n" +
+	"\bnikeName\x18\x01 \x01(\tR\bnikeName\x12\x18\n" +
+	"\aaccount\x18\x02 \x01(\tR\aaccount\x12\x1a\n" +
+	"\bpassword\x18\x03 \x01(\tR\bpassword\x12\x15\n" +
+	"\x06m_head\x18\x04 \x01(\tR\x05mHead\"K\n" +
+	"\vResRegister\x12\x18\n" +
+	"\asuccess\x18\x01 \x01(\bR\asuccess\x12\"\n" +
+	"\aerr_msg\x18\x02 \x01(\v2\t.MsgErrorR\x06errMsg\"#\n" +
+	"\tEnterHall\x12\x16\n" +
+	"\x06userId\x18\x01 \x01(\tR\x06userId\"L\n" +
+	"\fResEnterHall\x12\x18\n" +
+	"\asuccess\x18\x01 \x01(\bR\asuccess\x12\"\n" +
+	"\aerr_msg\x18\x02 \x01(\v2\t.MsgErrorR\x06errMsg\"#\n" +
+	"\tLeaveHall\x12\x16\n" +
+	"\x06userId\x18\x01 \x01(\tR\x06userId\"\x8d\x01\n" +
+	"\tMatchLudo\x123\n" +
+	"\x10select_room_type\x18\x01 \x01(\x0e2\t.roomTypeR\x0eselectRoomType\x12\x1d\n" +
+	"\n" +
+	"room_level\x18\x02 \x01(\x05R\troomLevel\x12,\n" +
+	"\fselect_color\x18\x03 \x01(\x0e2\t.roleTypeR\vselectColor\"k\n" +
+	"\fResMatchLudo\x12\x18\n" +
+	"\asuccess\x18\x01 \x01(\bR\asuccess\x12\"\n" +
+	"\aerr_msg\x18\x02 \x01(\v2\t.MsgErrorR\x06errMsg\x12\x1d\n" +
+	"\x04room\x18\x03 \x01(\v2\t.RoomInfoR\x04room\"F\n" +
+	"\bMsgError\x12\x1d\n" +
+	"\n" +
+	"error_code\x18\x01 \x01(\x05R\terrorCode\x12\x1b\n" +
+	"\terror_msg\x18\x02 \x01(\tR\berrorMsg\" \n" +
+	"\fResHeartBeat\x12\x10\n" +
+	"\x03msg\x18\x01 \x01(\tR\x03msg\" \n" +
+	"\fReqHeartBeat\x12\x10\n" +
+	"\x03msg\x18\x01 \x01(\tR\x03msg*K\n" +
+	"\broleType\x12\x15\n" +
+	"\x11ROLE_TYPE_UNKNOWN\x10\x00\x12\a\n" +
+	"\x03RED\x10\x01\x12\b\n" +
+	"\x04BLUE\x10\x04\x12\n" +
+	"\n" +
+	"\x06YELLOW\x10\x03\x12\t\n" +
+	"\x05GREEN\x10\x02*A\n" +
+	"\aOptType\x12\x14\n" +
+	"\x10OPT_TYPE_UNKNOWN\x10\x00\x12\x0f\n" +
+	"\vZHI_SHAI_ZI\x10\x01\x12\x0f\n" +
+	"\vSELECT_ROLE\x10\x02*@\n" +
+	"\broomType\x12\x15\n" +
+	"\x11ROOM_TYPE_UNKNOWN\x10\x00\x12\x0e\n" +
+	"\n" +
+	"SHUANG_REN\x10\x01\x12\r\n" +
+	"\tSIREN_REN\x10\x02*:\n" +
+	"\broomMode\x12\x15\n" +
+	"\x11ROOM_MODE_UNKNOWN\x10\x00\x12\n" +
+	"\n" +
+	"\x06REN_JI\x10\x01\x12\v\n" +
+	"\aWAN_JIA\x10\x02*+\n" +
+	"\broadType\x12\x15\n" +
+	"\x11ROAD_TYPE_UNKNOWN\x10\x00\x12\b\n" +
+	"\x04HOME\x10\x01*]\n" +
+	"\fplayerStatus\x12\x19\n" +
+	"\x15PLAYER_STATUS_UNKNOWN\x10\x00\x12\x10\n" +
+	"\fSZ_ANIMATION\x10\x01\x12\x10\n" +
+	"\fCOLOR_FINISH\x10\x02\x12\x0e\n" +
+	"\n" +
+	"COLOR_KICK\x10\x03*+\n" +
+	"\n" +
+	"roomStatus\x12\t\n" +
+	"\x05AWAIT\x10\x00\x12\t\n" +
+	"\x05START\x10\x01\x12\a\n" +
+	"\x03END\x10\x02B\aZ\x05./msgb\x06proto3"
+
+var (
+	file_common_proto_rawDescOnce sync.Once
+	file_common_proto_rawDescData []byte
+)
+
+func file_common_proto_rawDescGZIP() []byte {
+	file_common_proto_rawDescOnce.Do(func() {
+		file_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)))
+	})
+	return file_common_proto_rawDescData
+}
+
+var file_common_proto_enumTypes = make([]protoimpl.EnumInfo, 7)
+var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
+var file_common_proto_goTypes = []any{
+	(RoleType)(0),                // 0: roleType
+	(OptType)(0),                 // 1: OptType
+	(RoomType)(0),                // 2: roomType
+	(RoomMode)(0),                // 3: roomMode
+	(RoadType)(0),                // 4: roadType
+	(PlayerStatus)(0),            // 5: playerStatus
+	(RoomStatus)(0),              // 6: roomStatus
+	(*Round)(nil),                // 7: round
+	(*RoomInfo)(nil),             // 8: RoomInfo
+	(*RoleData)(nil),             // 9: RoleData
+	(*ColorData)(nil),            // 10: ColorData
+	(*MoveStepData)(nil),         // 11: MoveStepData
+	(*SendColorSz)(nil),          // 12: SendColorSz
+	(*SendRoleMove)(nil),         // 13: SendRoleMove
+	(*NotifyPlayerSzNumber)(nil), // 14: NotifyPlayerSzNumber
+	(*NotifyPlayerMove)(nil),     // 15: NotifyPlayerMove
+	(*NotifyPlayerOpt)(nil),      // 16: NotifyPlayerOpt
+	(*NotifyPlayerStatus)(nil),   // 17: NotifyPlayerStatus
+	(*NotifySettlement)(nil),     // 18: NotifySettlement
+	(*UserInfo)(nil),             // 19: UserInfo
+	(*ReqLogin)(nil),             // 20: ReqLogin
+	(*ResLogin)(nil),             // 21: ResLogin
+	(*ReqRegister)(nil),          // 22: ReqRegister
+	(*ResRegister)(nil),          // 23: ResRegister
+	(*EnterHall)(nil),            // 24: EnterHall
+	(*ResEnterHall)(nil),         // 25: ResEnterHall
+	(*LeaveHall)(nil),            // 26: LeaveHall
+	(*MatchLudo)(nil),            // 27: MatchLudo
+	(*ResMatchLudo)(nil),         // 28: ResMatchLudo
+	(*MsgError)(nil),             // 29: MsgError
+	(*ResHeartBeat)(nil),         // 30: ResHeartBeat
+	(*ReqHeartBeat)(nil),         // 31: ReqHeartBeat
+}
+var file_common_proto_depIdxs = []int32{
+	0,  // 0: round.m_color:type_name -> roleType
+	1,  // 1: round.opt:type_name -> OptType
+	9,  // 2: RoomInfo.roles:type_name -> RoleData
+	10, // 3: RoomInfo.colors:type_name -> ColorData
+	2,  // 4: RoomInfo.room_type:type_name -> roomType
+	3,  // 5: RoomInfo.room_mode:type_name -> roomMode
+	0,  // 6: RoomInfo.cur_round_color:type_name -> roleType
+	7,  // 7: RoomInfo.rounds:type_name -> round
+	10, // 8: RoomInfo.finish_colors:type_name -> ColorData
+	10, // 9: RoomInfo.kict_colors:type_name -> ColorData
+	6,  // 10: RoomInfo.room_status:type_name -> roomStatus
+	0,  // 11: RoleData.m_color:type_name -> roleType
+	4,  // 12: RoleData.m_cur_road:type_name -> roadType
+	0,  // 13: ColorData.m_color:type_name -> roleType
+	0,  // 14: SendColorSz.color:type_name -> roleType
+	0,  // 15: SendRoleMove.color:type_name -> roleType
+	0,  // 16: NotifyPlayerSzNumber.color:type_name -> roleType
+	29, // 17: NotifyPlayerSzNumber.err_msg:type_name -> MsgError
+	0,  // 18: NotifyPlayerMove.color:type_name -> roleType
+	11, // 19: NotifyPlayerMove.step:type_name -> MoveStepData
+	9,  // 20: NotifyPlayerMove.kick:type_name -> RoleData
+	0,  // 21: NotifyPlayerOpt.color:type_name -> roleType
+	1,  // 22: NotifyPlayerOpt.opt:type_name -> OptType
+	9,  // 23: NotifyPlayerOpt.canMoveRoles:type_name -> RoleData
+	0,  // 24: NotifyPlayerStatus.color:type_name -> roleType
+	5,  // 25: NotifyPlayerStatus.status:type_name -> playerStatus
+	10, // 26: NotifyPlayerStatus.colors:type_name -> ColorData
+	0,  // 27: NotifySettlement.color:type_name -> roleType
+	10, // 28: NotifySettlement.finish_colors:type_name -> ColorData
+	19, // 29: ResLogin.userInfo:type_name -> UserInfo
+	29, // 30: ResLogin.err_msg:type_name -> MsgError
+	29, // 31: ResRegister.err_msg:type_name -> MsgError
+	29, // 32: ResEnterHall.err_msg:type_name -> MsgError
+	2,  // 33: MatchLudo.select_room_type:type_name -> roomType
+	0,  // 34: MatchLudo.select_color:type_name -> roleType
+	29, // 35: ResMatchLudo.err_msg:type_name -> MsgError
+	8,  // 36: ResMatchLudo.room:type_name -> RoomInfo
+	37, // [37:37] is the sub-list for method output_type
+	37, // [37:37] is the sub-list for method input_type
+	37, // [37:37] is the sub-list for extension type_name
+	37, // [37:37] is the sub-list for extension extendee
+	0,  // [0:37] is the sub-list for field type_name
+}
+
+func init() { file_common_proto_init() }
+func file_common_proto_init() {
+	if File_common_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)),
+			NumEnums:      7,
+			NumMessages:   25,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_common_proto_goTypes,
+		DependencyIndexes: file_common_proto_depIdxs,
+		EnumInfos:         file_common_proto_enumTypes,
+		MessageInfos:      file_common_proto_msgTypes,
+	}.Build()
+	File_common_proto = out.File
+	file_common_proto_goTypes = nil
+	file_common_proto_depIdxs = nil
+}

+ 59 - 0
src/server/msg/msg.go

@@ -0,0 +1,59 @@
+package msg
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"strings"
+
+	"github.com/name5566/leaf/log"
+	"github.com/name5566/leaf/network/protobuf"
+)
+
+var Processor = protobuf.NewProcessor()
+
+type MsgInfo struct {
+	ID   uint16
+	Name string
+}
+
+var msgList []MsgInfo
+
+func init() {
+	Processor.Register(&ReqLogin{})
+	Processor.Register(&ResLogin{})
+	Processor.Register(&EnterHall{})
+	Processor.Register(&LeaveHall{})
+	Processor.Register(&MatchLudo{})
+	Processor.Register(&ResHeartBeat{})
+	Processor.Register(&ReqHeartBeat{})
+	Processor.Range(func(id uint16, t reflect.Type) {
+		log.Debug("消息ID: %d, 消息类型: %s\n", id, t.Elem().Name())
+		msgList = append(msgList, MsgInfo{
+			ID:   id,
+			Name: t.Elem().Name(),
+		})
+		// fmt.Printf("消息ID: %d, 消息类型: %s\n", id, t.Elem().Name())
+	})
+	generateTSCode()
+}
+
+func generateTSCode() {
+	var tsCode strings.Builder
+	tsCode.WriteString("// 自动生成的消息ID映射\n")
+	tsCode.WriteString("export enum MsgID {\n")
+
+	for _, msg := range msgList {
+		tsCode.WriteString(fmt.Sprintf("    %s = %d,\n", msg.Name, msg.ID))
+	}
+
+	tsCode.WriteString("}\n")
+
+	var path = "/home/zjh/teen_patti/bin/client_msg/msg.ts"
+	path = "/Users/xy/Desktop/ludo_server/bin/client_msg/msg.ts"
+	// 写入文件
+	err := os.WriteFile(path, []byte(tsCode.String()), 0644)
+	if err != nil {
+		log.Error("生成 TypeScript 代码失败: %v", err)
+	}
+}

+ 16 - 0
src/server/msg/test.proto

@@ -0,0 +1,16 @@
+syntax = "proto3";
+package msg;
+
+option go_package = "./msg";
+
+message Hello {
+	string name = 1;
+}
+
+message ReqLogin {
+	int64 user_id = 1;
+}
+
+message RespLogin {
+	int64 user_id = 1;
+}

BIN
src/server/server


+ 81 - 0
src/server/tools/time.go

@@ -0,0 +1,81 @@
+package tools
+
+import (
+	"sync"
+	"time"
+)
+
+// TimerEvent 定时事件结构
+type TimerEvent struct {
+	ID        string      // 事件ID
+	Timestamp int64       // 触发时间
+	Callback  func()      // 回调函数
+	Timer     *time.Timer // 定时器
+}
+
+// TimerManager 定时管理器
+type TimerManager struct {
+	events map[string]*TimerEvent
+	mutex  sync.RWMutex
+}
+
+// NewTimerManager 创建定时管理器
+func NewTimerManager() *TimerManager {
+	return &TimerManager{
+		events: make(map[string]*TimerEvent),
+	}
+}
+
+// AddEvent 添加定时事件
+func (tm *TimerManager) AddEvent(id string, delay time.Duration, callback func()) {
+	tm.mutex.Lock()
+	defer tm.mutex.Unlock()
+
+	// 创建定时器
+	timer := time.NewTimer(delay)
+	event := &TimerEvent{
+		ID:        id,
+		Timestamp: time.Now().Add(delay).Unix(),
+		Callback:  callback,
+		Timer:     timer,
+	}
+
+	// 保存事件
+	tm.events[id] = event
+
+	// 启动协程等待触发
+	go func() {
+		<-timer.C
+		tm.triggerEvent(id)
+	}()
+}
+
+// TriggerEvent 立即触发事件
+func (tm *TimerManager) TriggerEvent(id string) {
+	tm.mutex.Lock()
+	defer tm.mutex.Unlock()
+	tm.triggerEvent(id)
+}
+
+// 内部触发事件方法
+func (tm *TimerManager) triggerEvent(id string) {
+	if event, exists := tm.events[id]; exists {
+		// 停止定时器
+		event.Timer.Stop()
+		// 执行回调
+		event.Callback()
+		// 删除事件
+		delete(tm.events, id)
+	}
+}
+
+// RemoveEvent 移除事件
+func (tm *TimerManager) RemoveEvent(id string) {
+	tm.mutex.Lock()
+	defer tm.mutex.Unlock()
+
+	if event, exists := tm.events[id]; exists {
+		event.Timer.Stop()
+		delete(tm.events, id)
+	}
+}

+ 10 - 0
src/server/user/external.go

@@ -0,0 +1,10 @@
+package user
+
+import (
+	"server/user/internal"
+)
+
+var (
+	Module  = new(internal.Module)
+	ChanRPC = internal.ChanRPC
+)

+ 13 - 0
src/server/user/internal/handler.go

@@ -0,0 +1,13 @@
+package internal
+
+import (
+	"reflect"
+)
+
+func handleMsg(m interface{}, h interface{}) {
+	skeleton.RegisterChanRPC(reflect.TypeOf(m), h)
+}
+
+func init() {
+	// handleMsg(&msg.ReqLogin{}, loginHandler)
+}

+ 23 - 0
src/server/user/internal/module.go

@@ -0,0 +1,23 @@
+package internal
+
+import (
+	"github.com/name5566/leaf/module"
+	"server/base"
+)
+
+var (
+	skeleton = base.NewSkeleton()
+	ChanRPC  = skeleton.ChanRPCServer
+)
+
+type Module struct {
+	*module.Skeleton
+}
+
+func (m *Module) OnInit() {
+	m.Skeleton = skeleton
+}
+
+func (m *Module) OnDestroy() {
+
+}

+ 82 - 0
src/server/user/user.go

@@ -0,0 +1,82 @@
+package user
+
+import (
+	mongodbmgr "server/db/mongodb"
+	redismgr "server/db/redis"
+	"server/msg"
+
+	"github.com/name5566/leaf/log"
+)
+
+// 查找用户信息是否已经存在 Redis 中
+func GetUserInfoById(userId string) *msg.UserInfo {
+	// 从 Redis 中获取用户数据
+	userData, err := redismgr.GetUserInfoFromRedis(userId)
+	if err != nil {
+		log.Debug("Error fetching user info from Redis:", err)
+		return nil
+	}
+
+	// 如果 Redis 中有数据,直接返回
+	if userData != nil {
+		log.Debug("User found in Redis: %s", userId)
+		return userData
+	}
+
+	// 如果 Redis 中没有数据,检查 MongoDB
+	log.Debug("User not found in Redis, checking MongoDB: %s", userId)
+	userData, err = redismgr.GetUserInfoFromRedis(userId)
+	if err != nil {
+		log.Debug("Error fetching user info from MongoDB:", err)
+		return nil
+	}
+
+	// 如果 MongoDB 中也没有数据,执行新增操作
+	if userData == nil {
+		log.Debug("User not found in MongoDB, creating new user: %s", userId)
+		userData = &msg.UserInfo{
+			UserId: userId,
+		}
+
+		// 保存到 MongoDB 和 Redis
+		err = mongodbmgr.AddUser(userData)
+		if err != nil {
+			log.Debug("Error saving new user to MongoDB:", err)
+			return nil
+		}
+
+		err = redismgr.SaveUserInfoToRedis(userData)
+		if err != nil {
+			log.Debug("Error saving new user to Redis:", err)
+			return nil
+		}
+	}
+
+	return userData
+}
+
+// 减少用户积分
+func DecreaseUserPoint(decrementAmount int32, userId string) {
+	userData := GetUserInfoById(userId)
+	// 获取存储在 agent 中的 UserData
+	// 减少用户积分
+	// userData.Points -= decrementAmount
+	SetUserInfo(userData)
+	// 打印更新日志
+	log.Debug("User %s's points decreased by %d. New balance: %d", userId, decrementAmount, userData)
+}
+func SetUserInfo(userData *msg.UserInfo) {
+	redismgr.SaveUserInfoToRedis(userData)
+}
+
+// 更新用户积分
+func AddUserPoint(betAmount int32, userId string) {
+	// 查找用户数据
+	userData := GetUserInfoById(userId)
+
+	// 更新用户的积分
+	// userData.Points += betAmount
+	SetUserInfo(userData)
+	// 打印更新日志
+	log.Debug("User %s's points updated: %d", userId, userData)
+}

+ 17 - 0
src/server/user/userData.go

@@ -0,0 +1,17 @@
+package user
+
+// import (
+// 	"server/game/room"
+// )
+
+// // import "server/game/room"
+
+// type UserData struct {
+// 	Id              string
+// 	Status          int
+// 	Teen_Patti_Room *room.Room
+// 	Head_Image      string
+// 	Nickname        string
+// 	Room_id         string
+// 	Points          int32
+// }