├── .gitignore ├── README.md ├── account ├── common │ ├── config.go │ └── model.go ├── main │ ├── config.go │ ├── init.go │ └── main.go └── service │ └── handle.go ├── client ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── fish.tar.gz └── qg_副本.jpg ├── common ├── api │ └── thrift │ │ ├── account.thrift │ │ └── gen-go │ │ └── rpc │ │ ├── GoUnusedProtection__.go │ │ ├── account-consts.go │ │ ├── account.go │ │ └── user_service-remote │ │ └── user_service-remote.go ├── conf │ ├── account.conf │ ├── game.conf │ ├── hall.conf │ └── traces.json └── tools │ ├── aes.go │ ├── call_rpc.go │ ├── snowFlake.go │ └── tools.go ├── game ├── common │ └── config.go ├── controllers │ ├── create_public_room.go │ ├── create_room.go │ ├── enter_public_room.go │ ├── enter_room.go │ ├── get_server_info.go │ ├── is_room_running.go │ └── ping.go ├── main │ ├── config.go │ ├── init.go │ └── main.go ├── router │ └── router.go └── service │ ├── client.go │ ├── define.go │ ├── fish_utils.go │ ├── request.go │ └── room.go ├── go.mod ├── go.sum ├── hall ├── common │ └── config.go ├── controllers │ ├── enter_public_room.go │ ├── get_message.go │ ├── get_server_info.go │ ├── get_user_status.go │ ├── guest.go │ ├── login.go │ ├── qq_callback.go │ ├── qq_login.go │ └── register_game_server.go ├── main │ ├── config.go │ ├── init.go │ └── main.go └── router │ └── router.go ├── start_account.bat ├── start_fish.bat ├── start_hall.bat ├── wx.jpg └── z-start_all_server.bat /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.exe 3 | *.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 捕鱼 2 | 3 | 运行步骤: 4 | 5 | 1.下载源码: 6 | 7 | git clone https://github.com/dwg255/fish 8 | 9 | 2.编译: 10 | 11 | cd fish\ 12 | go build -o account.exe account\main\main.go account\main\init.go account\main\config.go 13 | go build -o hall.exe hall\main\main.go hall\main\init.go hall\main\config.go 14 | go build -o fish.exe game\main\main.go game\main\init.go game\main\config.go 15 | 16 | 3.解压客户端: 17 | tar -zxvf fish.tar.gz /var/www/html/client/fish 18 | 19 | 4.配置nginx: 20 | ``` 21 | server { 22 | listen 80; 23 | server_name fish.com; 24 | charset utf8; 25 | index index.html index.htm; 26 | location /qq { 27 | add_header Access-Control-Allow-Origin *; 28 | proxy_set_header X-Target $request_uri; 29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 30 | proxy_pass http://127.0.0.1:9000; 31 | } 32 | location / { 33 | root /var/www/html/client/fish; 34 | add_header Access-Control-Allow-Origin *; 35 | expires 7d; 36 | } 37 | } 38 | ``` 39 | 配置文件位置 /common/conf 内含redis配置和qq第三方登录配置,请自行修改。 40 | 41 | 5.在线示例: 42 | http://fish.blzz.shop 43 | 44 | --- 45 | 46 | License 47 | 48 | This project is released under the terms of the MIT license. See [LICENSE](LICENSE) for more 49 | information or see https://opensource.org/licenses/MIT. 50 | 51 | 52 | --- 53 | 54 | ![](https://github.com/dwg255/fish/blob/master/client/qg_%E5%89%AF%E6%9C%AC.jpg?raw=true) 55 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/1.jpg) 56 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/2.jpg) 57 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/3.jpg) 58 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/4.jpg) 59 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/5.jpg) 60 | ![](https://raw.githubusercontent.com/dwg255/fish/master/client/6.jpg) 61 | ![](https://github.com/dwg255/fish/blob/master/client/qg_%E5%89%AF%E6%9C%AC.jpg?raw=true) 62 | -------------------------------------------------------------------------------- /account/common/config.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/go-redis/redis" 5 | "github.com/jmoiron/sqlx" 6 | ) 7 | 8 | var ( 9 | AccountConf = &AccountServiceConf{ 10 | RedisConf: &RedisConf{}, 11 | MysqlConf: &MysqlConf{}, 12 | } 13 | ) 14 | 15 | type AccountServiceConf struct { 16 | AccountAesKey string 17 | ThriftPort int 18 | LogPath string 19 | LogLevel string 20 | RedisConf *RedisConf 21 | MysqlConf *MysqlConf 22 | } 23 | 24 | type RedisConf struct { 25 | RedisAddrs []string 26 | RedisKeyPrefix string 27 | //RedisPool *redis.ClusterClient 28 | RedisPool *redis.Client 29 | } 30 | 31 | type MysqlConf struct { 32 | MysqlAddr string 33 | MysqlUser string 34 | MysqlPassword string 35 | MysqlDatabase string 36 | Pool *sqlx.DB 37 | } 38 | -------------------------------------------------------------------------------- /account/common/model.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type FishUser struct { 4 | UserId int `json:"userid" dB:"UsERID"` 5 | Account string `json:"account" DB:"account"` 6 | Name string `json:"name" DB:"name"` 7 | Sex int `json:"sex" dB:"sex"` 8 | HeadImg string `json:"headimg" DB:"headimg"` 9 | Lv int `json:"lv" DB:"LV"` 10 | Exp int `json:"exP" Db:"exP"` 11 | Coins int `json:"coins" db:"coins"` 12 | Vip int `json:"vip" db:"vip"` 13 | Money int `json:"money" DB:"money"` 14 | Gems int `json:"gems" db:"gems"` 15 | RoomId string `json:"roomid" DB:"roomid"` 16 | History string `json:"history" dB:"history"` 17 | Power int `json:"power" DB:"power"` 18 | ReNameCount int `json:"renamecount" db:"renamecount"` 19 | ReHeadCount int `json:"reheadcount" db:"reheadcount"` 20 | PropId int `json:"propid" db:"propid"` 21 | } 22 | 23 | type InvestUserStake struct { 24 | Id int `json:"id" db:"id"` 25 | GameTimesId string `json:"game_times_id" db:"game_times_id"` 26 | Periods int `json:"record_id" db:"periods"` 27 | RoomId int `json:"room_id" db:"room_id"` 28 | RoomType int `json:"room_type" db:"room_type"` 29 | UserId int `json:"user_id" db:"user_id"` 30 | Nickname string `json:"nickname" db:"nickname"` 31 | UserAllStake int `json:"stake_gold" db:"user_all_stake"` 32 | WinGold int `json:"get_gold" db:"get_gold"` 33 | StakeDetail string `json:"stake_detail" db:"stake_detail"` 34 | GameResult int `json:"game_result" db:"game_result"` 35 | Pool int `json:"game_pool" db:"game_pool"` 36 | StakeTime string `json:"stake_time" db:"last_stake_time"` 37 | } 38 | -------------------------------------------------------------------------------- /account/main/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fish/account/common" 5 | "fmt" 6 | "github.com/astaxie/beego/config" 7 | "github.com/astaxie/beego/logs" 8 | ) 9 | 10 | func initConf() (err error) { 11 | conf, err := config.NewConfig("ini", "./common/conf/account.conf") 12 | if err != nil { 13 | fmt.Println("new config failed,err:", err) 14 | return 15 | } 16 | 17 | common.AccountConf.ThriftPort, err = conf.Int("account_port") 18 | if err != nil { 19 | return 20 | } 21 | common.AccountConf.AccountAesKey = conf.String("account_aes_key") 22 | if common.AccountConf.AccountAesKey == "" || len(common.AccountConf.AccountAesKey) < 16 { 23 | return fmt.Errorf("conf err: invalid account_aes_key :%v", common.AccountConf.AccountAesKey) 24 | } 25 | logs.Debug("account_aes_key :%v",common.AccountConf.AccountAesKey) 26 | 27 | common.AccountConf.LogPath = conf.String("log_path") 28 | if common.AccountConf.LogPath == "" { 29 | return fmt.Errorf("conf err: log_path is null") 30 | } 31 | 32 | common.AccountConf.LogLevel = conf.String("log_level") 33 | if common.AccountConf.LogLevel == "" { 34 | return fmt.Errorf("conf err: log_level is null") 35 | } 36 | 37 | //redis配置 38 | common.AccountConf.RedisConf.RedisAddrs = conf.Strings("redis_addrs") 39 | if len(common.AccountConf.RedisConf.RedisAddrs) == 0 { 40 | return fmt.Errorf("conf err: redis addr is null") 41 | } 42 | common.AccountConf.RedisConf.RedisKeyPrefix = conf.String("redis_key_prefix") 43 | if len(common.AccountConf.RedisConf.RedisKeyPrefix) == 0 { 44 | return fmt.Errorf("conf err: redis_key_prefix is null") 45 | } 46 | 47 | //mysql配置 48 | common.AccountConf.MysqlConf.MysqlAddr = conf.String("mysql_addr") 49 | if len(common.AccountConf.MysqlConf.MysqlAddr) == 0 { 50 | return fmt.Errorf("conf err: mysql_addr is null") 51 | } 52 | common.AccountConf.MysqlConf.MysqlUser = conf.String("mysql_user") 53 | if len(common.AccountConf.MysqlConf.MysqlUser) == 0 { 54 | return fmt.Errorf("conf err: mysql_user is null") 55 | } 56 | common.AccountConf.MysqlConf.MysqlPassword = conf.String("mysql_password") 57 | if len(common.AccountConf.MysqlConf.MysqlPassword) == 0 { 58 | return fmt.Errorf("conf err: mysql_password is null") 59 | } 60 | common.AccountConf.MysqlConf.MysqlDatabase = conf.String("mysql_db") 61 | if len(common.AccountConf.MysqlConf.MysqlDatabase) == 0 { 62 | return fmt.Errorf("conf err: mysql_db is null") 63 | } 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /account/main/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fish/account/common" 6 | "fmt" 7 | "github.com/astaxie/beego/logs" 8 | "github.com/go-redis/redis" 9 | _ "github.com/go-sql-driver/mysql" 10 | "github.com/jmoiron/sqlx" 11 | ) 12 | 13 | func conversionLogLevel(logLevel string) int { 14 | switch logLevel { 15 | case "debug": 16 | return logs.LevelDebug 17 | case "warn": 18 | return logs.LevelWarn 19 | case "info": 20 | return logs.LevelInfo 21 | case "trace": 22 | return logs.LevelTrace 23 | } 24 | return logs.LevelDebug 25 | } 26 | 27 | func initLogger() (err error) { 28 | config := make(map[string]interface{}) 29 | config["filename"] = common.AccountConf.LogPath 30 | config["level"] = conversionLogLevel(common.AccountConf.LogLevel) 31 | 32 | configStr, err := json.Marshal(config) 33 | if err != nil { 34 | return 35 | } 36 | err = logs.SetLogger(logs.AdapterFile, string(configStr)) 37 | return 38 | } 39 | 40 | func initMysql() (err error) { 41 | conf := common.AccountConf.MysqlConf 42 | dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s", conf.MysqlUser, conf.MysqlPassword, conf.MysqlAddr, conf.MysqlDatabase) 43 | logs.Debug(dsn) 44 | database, err := sqlx.Open("mysql", dsn) 45 | if err != nil { 46 | return 47 | } 48 | common.AccountConf.MysqlConf.Pool = database 49 | return 50 | } 51 | 52 | func initRedis() (err error) { 53 | //client := redis.NewClusterClient(&redis.ClusterOptions{ 54 | // Addrs: common.AccountConf.RedisConf.RedisAddrs, 55 | //}) 56 | //_, err = client.Ping().Result() 57 | //if err != nil { 58 | // return 59 | //} 60 | //common.AccountConf.RedisConf.RedisPool = client 61 | client := redis.NewClient(&redis.Options{ 62 | Addr: "localhost:6379", 63 | Password: "", 64 | DB: 0, 65 | }) 66 | common.AccountConf.RedisConf.RedisPool = client 67 | return 68 | } 69 | 70 | func initSec() (err error) { 71 | err = initLogger() 72 | if err != nil { 73 | return 74 | } 75 | 76 | err = initRedis() 77 | if err != nil { 78 | return 79 | } 80 | 81 | /*err = initMysql() 82 | if err != nil { 83 | return 84 | }*/ 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /account/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fish/account/common" 5 | "fish/account/service" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fmt" 8 | "github.com/apache/thrift/lib/go/thrift" 9 | "github.com/astaxie/beego/logs" 10 | ) 11 | 12 | func main() { 13 | err := initConf() 14 | if err != nil { 15 | logs.Error("init conf err:%v",err) 16 | return 17 | } 18 | logs.Debug("init conf success") 19 | service.InitAesTool() 20 | err = initSec() 21 | if err != nil { 22 | logs.Error("initSec err:%v",err) 23 | return 24 | } 25 | logs.Debug("init sec success") 26 | 27 | port := fmt.Sprintf(":%d",common.AccountConf.ThriftPort) 28 | transport, err := thrift.NewTServerSocket(port) 29 | if err != nil { 30 | panic(err) 31 | } 32 | handler := &service.UserServer{} 33 | processor := rpc.NewUserServiceProcessor(handler) 34 | 35 | transportFactory := thrift.NewTBufferedTransportFactory(8192) 36 | protocolFactory := thrift.NewTCompactProtocolFactory() 37 | 38 | server := thrift.NewTSimpleServer4( 39 | processor, 40 | transport, 41 | transportFactory, 42 | protocolFactory, 43 | ) 44 | logs.Debug("account server %s",port) 45 | if err := server.Serve(); err != nil { 46 | panic(err) 47 | } 48 | } -------------------------------------------------------------------------------- /account/service/handle.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "fish/account/common" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fmt" 9 | "github.com/astaxie/beego/logs" 10 | "math/rand" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | var ( 17 | redisConf = common.AccountConf.RedisConf 18 | aesTool *tools.AesEncrypt 19 | ) 20 | 21 | type UserServer struct { 22 | } 23 | 24 | func InitAesTool() { 25 | var err error 26 | if aesTool, err = tools.NewAesTool(common.AccountConf.AccountAesKey); err != nil { 27 | panic("new aes tool err: " + err.Error()) 28 | } 29 | } 30 | 31 | func (p *UserServer) GetUserInfoByOpenId(ctx context.Context, openId string) (r *rpc.Result_, err error) { 32 | logs.Debug("getUserInfoByOpenId openId: %v", openId) 33 | var existsUserId string 34 | var userId int 35 | if existsUserId, err = redisConf.RedisPool.HGet(redisConf.RedisKeyPrefix+"open_id2user_id", openId).Result(); err == nil { 36 | if userId, err = strconv.Atoi(existsUserId); err == nil { 37 | return p.GetUserInfoById(ctx, int32(userId)) 38 | } 39 | } else { 40 | r = &rpc.Result_{ 41 | Code: rpc.ErrorCode_UserNotExists, 42 | } 43 | err = fmt.Errorf("user openId=%v not exists", openId) 44 | } 45 | return 46 | } 47 | 48 | func (p *UserServer) CreateQQUser(ctx context.Context, userInfo *rpc.UserInfo) (r *rpc.Result_, err error) { 49 | logs.Debug("CreateQQUser nickName: %v", userInfo.NickName) 50 | var nextUserId int64 51 | var existsUserId string 52 | var userId int 53 | if existsUserId, err = redisConf.RedisPool.HGet(redisConf.RedisKeyPrefix+"open_id2user_id", userInfo.QqInfo.OpenId).Result(); err == nil { 54 | if userId, err = strconv.Atoi(existsUserId); err == nil { 55 | return p.GetUserInfoById(ctx, int32(userId)) 56 | } 57 | } else { 58 | nextUserId, err = redisConf.RedisPool.Incr(redisConf.RedisKeyPrefix + "userId").Result() 59 | token := "" 60 | registerTime := time.Now() 61 | if token, err = aesTool.Encrypt(strconv.Itoa(int(nextUserId)) + "-" + strconv.Itoa(int(registerTime.Unix()))); err == nil { 62 | rand.Seed(time.Now().UnixNano()) 63 | userInfoRedisMap := map[string]interface{}{ 64 | "UserId": nextUserId, 65 | "UserName": userInfo.UserName, 66 | "NickName": userInfo.NickName, 67 | "Sex": userInfo.Sex, 68 | "HeadImg": userInfo.HeadImg, 69 | "Lv": userInfo.Lv, 70 | "Exp": userInfo.Exp, 71 | "Vip": userInfo.Vip, //VIP级别随机给吧, 72 | "Gems": userInfo.Gems, 73 | "RoomId": 0, 74 | "Power": userInfo.Power, 75 | "ReNameCount": userInfo.ReNameCount, 76 | "ReHeadCount": userInfo.ReHeadCount, 77 | "RegisterDate": registerTime.Format("2006-01-02 15:04:05"), 78 | "Ice": 10, 79 | "Token": token, 80 | "openId": userInfo.QqInfo.OpenId, 81 | "FigureUrl": userInfo.QqInfo.FigureUrl, 82 | "Province": userInfo.QqInfo.Province, 83 | "City": userInfo.QqInfo.City, 84 | "TotalSpending": userInfo.QqInfo.TotalSpending, 85 | } 86 | if _, err = redisConf.RedisPool.HMSet(redisConf.RedisKeyPrefix+strconv.Itoa(int(nextUserId)), userInfoRedisMap).Result(); err == nil { 87 | if _, err = redisConf.RedisPool.HSet(redisConf.RedisKeyPrefix+"open_id2user_id", userInfo.QqInfo.OpenId, nextUserId).Result(); err == nil { 88 | userInfo.UserId = nextUserId 89 | userInfo.Token = token 90 | r = &rpc.Result_{ 91 | Code: rpc.ErrorCode_Success, 92 | UserObj: userInfo, 93 | } 94 | return 95 | } 96 | } 97 | } 98 | } 99 | return 100 | } 101 | 102 | func (p *UserServer) CreateNewUser(ctx context.Context, nickName string, avatarAuto string, gold int64) (r *rpc.Result_, err error) { 103 | logs.Debug("CreateNewUser nickName: %v", nickName) 104 | var nextUserId int64 105 | nextUserId, err = redisConf.RedisPool.Incr(redisConf.RedisKeyPrefix + "userId").Result() 106 | token := "" 107 | registerTime := time.Now() 108 | if token, err = aesTool.Encrypt(strconv.Itoa(int(nextUserId)) + "-" + strconv.Itoa(int(registerTime.Unix()))); err == nil { 109 | rand.Seed(time.Now().UnixNano()) 110 | vip := int8(rand.Intn(7)) 111 | userInfoRedisMap := map[string]interface{}{ 112 | "UserId": nextUserId, 113 | "UserName": nickName, 114 | "NickName": nickName, 115 | "Sex": 0, 116 | "HeadImg": 1, 117 | "Lv": 1, 118 | "Exp": 0, 119 | "Vip": vip, //VIP级别随机给吧, 120 | "Gems": gold, 121 | "RoomId": 0, 122 | "Power": 0, 123 | "ReNameCount": 0, 124 | "ReHeadCount": 0, 125 | "RegisterDate": registerTime.Format("2006-01-02 15:04:05"), 126 | "Ice": 10, 127 | "Token": token, 128 | } 129 | if _, err = redisConf.RedisPool.HMSet(redisConf.RedisKeyPrefix+strconv.Itoa(int(nextUserId)), userInfoRedisMap).Result(); err == nil { 130 | 131 | r = &rpc.Result_{ 132 | Code: rpc.ErrorCode_Success, 133 | UserObj: &rpc.UserInfo{ 134 | UserId: nextUserId, 135 | UserName: nickName, 136 | NickName: nickName, 137 | Sex: 0, 138 | HeadImg: "1", 139 | Lv: 1, 140 | Exp: 0, 141 | Vip: vip, //VIP级别随机给吧 142 | Gems: gold, 143 | RoomId: 0, 144 | Power: 0, 145 | ReNameCount: 0, 146 | ReHeadCount: 0, 147 | RegisterDate: registerTime.Format("2006-01-02 15:04:05"), 148 | Ice: 10, 149 | Token: token, 150 | }, 151 | } 152 | return 153 | } 154 | } 155 | return 156 | } 157 | 158 | func (p *UserServer) GetUserInfoById(ctx context.Context, userId int32) (r *rpc.Result_, err error) { 159 | logs.Debug("GetUserInfoById: %v", userId) 160 | result, err := redisConf.RedisPool.HGetAll(redisConf.RedisKeyPrefix + strconv.Itoa(int(userId))).Result() 161 | if err != nil { 162 | return r, err 163 | } 164 | var Lv, Vip, Gems, RoomId, Power, Ice int 165 | if Lv, err = strconv.Atoi(result["Lv"]); err != nil { 166 | return r, err 167 | } 168 | if Vip, err = strconv.Atoi(result["Vip"]); err != nil { 169 | return r, err 170 | } 171 | if RoomId, err = strconv.Atoi(result["RoomId"]); err != nil { 172 | return r, err 173 | } 174 | if Gems, err = strconv.Atoi(result["Gems"]); err != nil { 175 | return r, err 176 | } 177 | if Power, err = strconv.Atoi(result["Power"]); err != nil { 178 | return r, err 179 | } 180 | if Ice, err = strconv.Atoi(result["Ice"]); err != nil { 181 | return r, err 182 | } 183 | 184 | r = &rpc.Result_{ 185 | Code: rpc.ErrorCode_Success, 186 | UserObj: &rpc.UserInfo{ 187 | UserId: int64(userId), 188 | UserName: result["UserName"], 189 | NickName: result["NickName"], 190 | Sex: 0, 191 | HeadImg: result["HeadImg"], 192 | Lv: int32(Lv), 193 | Exp: 0, 194 | Vip: int8(Vip), 195 | Gems: int64(Gems), 196 | RoomId: int64(RoomId), 197 | Power: int64(Power), 198 | ReNameCount: 0, 199 | ReHeadCount: 0, 200 | RegisterDate: result["RegisterDate"], 201 | Ice: int64(Ice), 202 | Token: result["Token"], 203 | }, 204 | } 205 | return 206 | } 207 | 208 | func (p *UserServer) GetUserInfoByToken(ctx context.Context, token string) (r *rpc.Result_, err error) { 209 | logs.Debug("GetUserInfoByToken: %v", token) 210 | userIdStr := "" 211 | if userIdStr, err = aesTool.Decrypt(token); err == nil { 212 | userId := 0 213 | tokenSplit := strings.Split(userIdStr, "-") 214 | if len(tokenSplit) > 1 { 215 | if userId, err = strconv.Atoi(tokenSplit[0]); err == nil { 216 | return p.GetUserInfoById(context.Background(), int32(userId)) 217 | } 218 | } 219 | } 220 | return 221 | } 222 | 223 | func (p *UserServer) ModifyUserInfoById(ctx context.Context, behavior string, userId int32, propType rpc.ModifyPropType, incr int64) (r *rpc.Result_, err error) { 224 | logs.Debug("ModifyUserInfoById: %v", behavior) 225 | var exists int64 226 | userInfoKey := redisConf.RedisKeyPrefix + strconv.Itoa(int(userId)) 227 | if exists, err = common.AccountConf.RedisConf.RedisPool.Exists(userInfoKey).Result(); err == nil { 228 | if exists == 1 { 229 | switch propType { 230 | case rpc.ModifyPropType_gems: 231 | common.AccountConf.RedisConf.RedisPool.HIncrBy(userInfoKey, "Gems", incr) 232 | case rpc.ModifyPropType_ice: 233 | common.AccountConf.RedisConf.RedisPool.HIncrBy(userInfoKey, "Ice", incr) 234 | case rpc.ModifyPropType_power: 235 | common.AccountConf.RedisConf.RedisPool.HIncrBy(userInfoKey, "Power", incr) 236 | case rpc.ModifyPropType_roomId: 237 | common.AccountConf.RedisConf.RedisPool.HIncrBy(userInfoKey, "RoomId", incr) 238 | } 239 | return p.GetUserInfoById(context.Background(), userId) 240 | } 241 | err = fmt.Errorf("user [%d] doesnot exists", userId) 242 | } 243 | return 244 | } 245 | 246 | func (p *UserServer) GetMessage(ctx context.Context, messageType string) (r string, err error) { 247 | logs.Debug("GetMessage messageType: %v", messageType) 248 | var redisErr error 249 | if messageType == "notice" { 250 | r, redisErr = redisConf.RedisPool.Get(redisConf.RedisKeyPrefix + "notice").Result() 251 | if r == "" || redisErr != nil { 252 | r = "个人开发,仅可用于学习研究。" 253 | } 254 | } else { 255 | r, redisErr = redisConf.RedisPool.Get(redisConf.RedisKeyPrefix + "fkgm").Result() 256 | if r == "" || redisErr != nil { 257 | r = "爸爸爱你" 258 | } 259 | } 260 | return 261 | } 262 | func (p *UserServer) RenameUserById(ctx context.Context, userId int32, NewName string) (r *rpc.Result_, err error) { 263 | return 264 | } 265 | -------------------------------------------------------------------------------- /client/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/1.jpg -------------------------------------------------------------------------------- /client/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/2.jpg -------------------------------------------------------------------------------- /client/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/3.jpg -------------------------------------------------------------------------------- /client/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/4.jpg -------------------------------------------------------------------------------- /client/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/5.jpg -------------------------------------------------------------------------------- /client/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/6.jpg -------------------------------------------------------------------------------- /client/fish.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/fish.tar.gz -------------------------------------------------------------------------------- /client/qg_副本.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/client/qg_副本.jpg -------------------------------------------------------------------------------- /common/api/thrift/account.thrift: -------------------------------------------------------------------------------- 1 | namespace go rpc 2 | 3 | enum ErrorCode{ 4 | Success=0 5 | ServerError=5000, 6 | VerifyError=5001, 7 | UserNotExists=5002, 8 | } 9 | 10 | enum ModifyPropType{ 11 | gems = 0 12 | roomId = 1 13 | power = 2 14 | ice = 3 15 | } 16 | 17 | struct qqInfo{ 18 | 1:string openId 19 | 2:string figureUrl 20 | 3:string province 21 | 4:string city 22 | 5:i64 totalSpending 23 | } 24 | struct UserInfo{ 25 | 1: i64 userId 26 | 2: string userName 27 | 3: string nickName 28 | 4: i8 sex 29 | 5: string headImg 30 | 6: i32 lv 31 | 7: i64 exp 32 | 8: i8 vip 33 | 9: i64 gems 34 | 10: i64 roomId 35 | 11: i64 power 36 | 12: i8 reNameCount 37 | 13: i8 reHeadCount 38 | 14: string registerDate 39 | 15: i64 ice 40 | 16: string token 41 | 17: qqInfo qqInfo 42 | } 43 | 44 | struct Result{ 45 | 1: ErrorCode code 46 | 2: UserInfo user_obj 47 | } 48 | 49 | service UserService { 50 | 51 | //创建临时用户 52 | Result createNewUser(1: string nickName 2:string avatarAuto 3: i64 gold )//初始金币 53 | 54 | //创建QQ用户 55 | Result createQQUser(1: UserInfo UserInfo) 56 | 57 | //使用openId获取用户 58 | Result getUserInfoByOpenId(1:string openId) 59 | 60 | //获取用户信息 BY userId 61 | Result getUserInfoById(1:i32 userId) 62 | 63 | //获取用户信息 BY token 64 | Result getUserInfoByToken(1:string token) 65 | 66 | //修改用户金币 67 | Result modifyUserInfoById(1:string behavior, 2:i32 userId, 3: ModifyPropType propType, 4: i64 incr) 68 | Result RenameUserById(1:i32 userId,2:string NewName) 69 | string getMessage(1 :string messageType) 70 | } 71 | -------------------------------------------------------------------------------- /common/api/thrift/gen-go/rpc/GoUnusedProtection__.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.12.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package rpc 5 | 6 | var GoUnusedProtection__ int; 7 | 8 | -------------------------------------------------------------------------------- /common/api/thrift/gen-go/rpc/account-consts.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.12.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package rpc 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "reflect" 10 | "fmt" 11 | "github.com/apache/thrift/lib/go/thrift" 12 | ) 13 | 14 | // (needed to ensure safety because of naive import list construction.) 15 | var _ = thrift.ZERO 16 | var _ = fmt.Printf 17 | var _ = context.Background 18 | var _ = reflect.DeepEqual 19 | var _ = bytes.Equal 20 | 21 | 22 | func init() { 23 | } 24 | 25 | -------------------------------------------------------------------------------- /common/api/thrift/gen-go/rpc/user_service-remote/user_service-remote.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.12.0) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package main 5 | 6 | import ( 7 | "context" 8 | "flag" 9 | "fmt" 10 | "math" 11 | "net" 12 | "net/url" 13 | "os" 14 | "strconv" 15 | "strings" 16 | "github.com/apache/thrift/lib/go/thrift" 17 | "rpc" 18 | ) 19 | 20 | 21 | func Usage() { 22 | fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") 23 | flag.PrintDefaults() 24 | fmt.Fprintln(os.Stderr, "\nFunctions:") 25 | fmt.Fprintln(os.Stderr, " Result createNewUser(string nickName, string avatarAuto, i64 gold)") 26 | fmt.Fprintln(os.Stderr, " Result createQQUser(UserInfo UserInfo)") 27 | fmt.Fprintln(os.Stderr, " Result getUserInfoByOpenId(string openId)") 28 | fmt.Fprintln(os.Stderr, " Result getUserInfoById(i32 userId)") 29 | fmt.Fprintln(os.Stderr, " Result getUserInfoByToken(string token)") 30 | fmt.Fprintln(os.Stderr, " Result modifyUserInfoById(string behavior, i32 userId, ModifyPropType propType, i64 incr)") 31 | fmt.Fprintln(os.Stderr, " Result RenameUserById(i32 userId, string NewName)") 32 | fmt.Fprintln(os.Stderr, " string getMessage(string messageType)") 33 | fmt.Fprintln(os.Stderr) 34 | os.Exit(0) 35 | } 36 | 37 | type httpHeaders map[string]string 38 | 39 | func (h httpHeaders) String() string { 40 | var m map[string]string = h 41 | return fmt.Sprintf("%s", m) 42 | } 43 | 44 | func (h httpHeaders) Set(value string) error { 45 | parts := strings.Split(value, ": ") 46 | if len(parts) != 2 { 47 | return fmt.Errorf("header should be of format 'Key: Value'") 48 | } 49 | h[parts[0]] = parts[1] 50 | return nil 51 | } 52 | 53 | func main() { 54 | flag.Usage = Usage 55 | var host string 56 | var port int 57 | var protocol string 58 | var urlString string 59 | var framed bool 60 | var useHttp bool 61 | headers := make(httpHeaders) 62 | var parsedUrl *url.URL 63 | var trans thrift.TTransport 64 | _ = strconv.Atoi 65 | _ = math.Abs 66 | flag.Usage = Usage 67 | flag.StringVar(&host, "h", "localhost", "Specify host and port") 68 | flag.IntVar(&port, "p", 9090, "Specify port") 69 | flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") 70 | flag.StringVar(&urlString, "u", "", "Specify the url") 71 | flag.BoolVar(&framed, "framed", false, "Use framed transport") 72 | flag.BoolVar(&useHttp, "http", false, "Use http") 73 | flag.Var(headers, "H", "Headers to set on the http(s) request (e.g. -H \"Key: Value\")") 74 | flag.Parse() 75 | 76 | if len(urlString) > 0 { 77 | var err error 78 | parsedUrl, err = url.Parse(urlString) 79 | if err != nil { 80 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) 81 | flag.Usage() 82 | } 83 | host = parsedUrl.Host 84 | useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" || parsedUrl.Scheme == "https" 85 | } else if useHttp { 86 | _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) 87 | if err != nil { 88 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) 89 | flag.Usage() 90 | } 91 | } 92 | 93 | cmd := flag.Arg(0) 94 | var err error 95 | if useHttp { 96 | trans, err = thrift.NewTHttpClient(parsedUrl.String()) 97 | if len(headers) > 0 { 98 | httptrans := trans.(*thrift.THttpClient) 99 | for key, value := range headers { 100 | httptrans.SetHeader(key, value) 101 | } 102 | } 103 | } else { 104 | portStr := fmt.Sprint(port) 105 | if strings.Contains(host, ":") { 106 | host, portStr, err = net.SplitHostPort(host) 107 | if err != nil { 108 | fmt.Fprintln(os.Stderr, "error with host:", err) 109 | os.Exit(1) 110 | } 111 | } 112 | trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) 113 | if err != nil { 114 | fmt.Fprintln(os.Stderr, "error resolving address:", err) 115 | os.Exit(1) 116 | } 117 | if framed { 118 | trans = thrift.NewTFramedTransport(trans) 119 | } 120 | } 121 | if err != nil { 122 | fmt.Fprintln(os.Stderr, "Error creating transport", err) 123 | os.Exit(1) 124 | } 125 | defer trans.Close() 126 | var protocolFactory thrift.TProtocolFactory 127 | switch protocol { 128 | case "compact": 129 | protocolFactory = thrift.NewTCompactProtocolFactory() 130 | break 131 | case "simplejson": 132 | protocolFactory = thrift.NewTSimpleJSONProtocolFactory() 133 | break 134 | case "json": 135 | protocolFactory = thrift.NewTJSONProtocolFactory() 136 | break 137 | case "binary", "": 138 | protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() 139 | break 140 | default: 141 | fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) 142 | Usage() 143 | os.Exit(1) 144 | } 145 | iprot := protocolFactory.GetProtocol(trans) 146 | oprot := protocolFactory.GetProtocol(trans) 147 | client := rpc.NewUserServiceClient(thrift.NewTStandardClient(iprot, oprot)) 148 | if err := trans.Open(); err != nil { 149 | fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) 150 | os.Exit(1) 151 | } 152 | 153 | switch cmd { 154 | case "createNewUser": 155 | if flag.NArg() - 1 != 3 { 156 | fmt.Fprintln(os.Stderr, "CreateNewUser requires 3 args") 157 | flag.Usage() 158 | } 159 | argvalue0 := flag.Arg(1) 160 | value0 := argvalue0 161 | argvalue1 := flag.Arg(2) 162 | value1 := argvalue1 163 | argvalue2, err20 := (strconv.ParseInt(flag.Arg(3), 10, 64)) 164 | if err20 != nil { 165 | Usage() 166 | return 167 | } 168 | value2 := argvalue2 169 | fmt.Print(client.CreateNewUser(context.Background(), value0, value1, value2)) 170 | fmt.Print("\n") 171 | break 172 | case "createQQUser": 173 | if flag.NArg() - 1 != 1 { 174 | fmt.Fprintln(os.Stderr, "CreateQQUser requires 1 args") 175 | flag.Usage() 176 | } 177 | arg21 := flag.Arg(1) 178 | mbTrans22 := thrift.NewTMemoryBufferLen(len(arg21)) 179 | defer mbTrans22.Close() 180 | _, err23 := mbTrans22.WriteString(arg21) 181 | if err23 != nil { 182 | Usage() 183 | return 184 | } 185 | factory24 := thrift.NewTJSONProtocolFactory() 186 | jsProt25 := factory24.GetProtocol(mbTrans22) 187 | argvalue0 := rpc.NewUserInfo() 188 | err26 := argvalue0.Read(jsProt25) 189 | if err26 != nil { 190 | Usage() 191 | return 192 | } 193 | value0 := argvalue0 194 | fmt.Print(client.CreateQQUser(context.Background(), value0)) 195 | fmt.Print("\n") 196 | break 197 | case "getUserInfoByOpenId": 198 | if flag.NArg() - 1 != 1 { 199 | fmt.Fprintln(os.Stderr, "GetUserInfoByOpenId requires 1 args") 200 | flag.Usage() 201 | } 202 | argvalue0 := flag.Arg(1) 203 | value0 := argvalue0 204 | fmt.Print(client.GetUserInfoByOpenId(context.Background(), value0)) 205 | fmt.Print("\n") 206 | break 207 | case "getUserInfoById": 208 | if flag.NArg() - 1 != 1 { 209 | fmt.Fprintln(os.Stderr, "GetUserInfoById requires 1 args") 210 | flag.Usage() 211 | } 212 | tmp0, err28 := (strconv.Atoi(flag.Arg(1))) 213 | if err28 != nil { 214 | Usage() 215 | return 216 | } 217 | argvalue0 := int32(tmp0) 218 | value0 := argvalue0 219 | fmt.Print(client.GetUserInfoById(context.Background(), value0)) 220 | fmt.Print("\n") 221 | break 222 | case "getUserInfoByToken": 223 | if flag.NArg() - 1 != 1 { 224 | fmt.Fprintln(os.Stderr, "GetUserInfoByToken requires 1 args") 225 | flag.Usage() 226 | } 227 | argvalue0 := flag.Arg(1) 228 | value0 := argvalue0 229 | fmt.Print(client.GetUserInfoByToken(context.Background(), value0)) 230 | fmt.Print("\n") 231 | break 232 | case "modifyUserInfoById": 233 | if flag.NArg() - 1 != 4 { 234 | fmt.Fprintln(os.Stderr, "ModifyUserInfoById requires 4 args") 235 | flag.Usage() 236 | } 237 | argvalue0 := flag.Arg(1) 238 | value0 := argvalue0 239 | tmp1, err31 := (strconv.Atoi(flag.Arg(2))) 240 | if err31 != nil { 241 | Usage() 242 | return 243 | } 244 | argvalue1 := int32(tmp1) 245 | value1 := argvalue1 246 | tmp2, err := (strconv.Atoi(flag.Arg(3))) 247 | if err != nil { 248 | Usage() 249 | return 250 | } 251 | argvalue2 := rpc.ModifyPropType(tmp2) 252 | value2 := argvalue2 253 | argvalue3, err32 := (strconv.ParseInt(flag.Arg(4), 10, 64)) 254 | if err32 != nil { 255 | Usage() 256 | return 257 | } 258 | value3 := argvalue3 259 | fmt.Print(client.ModifyUserInfoById(context.Background(), value0, value1, value2, value3)) 260 | fmt.Print("\n") 261 | break 262 | case "RenameUserById": 263 | if flag.NArg() - 1 != 2 { 264 | fmt.Fprintln(os.Stderr, "RenameUserById requires 2 args") 265 | flag.Usage() 266 | } 267 | tmp0, err33 := (strconv.Atoi(flag.Arg(1))) 268 | if err33 != nil { 269 | Usage() 270 | return 271 | } 272 | argvalue0 := int32(tmp0) 273 | value0 := argvalue0 274 | argvalue1 := flag.Arg(2) 275 | value1 := argvalue1 276 | fmt.Print(client.RenameUserById(context.Background(), value0, value1)) 277 | fmt.Print("\n") 278 | break 279 | case "getMessage": 280 | if flag.NArg() - 1 != 1 { 281 | fmt.Fprintln(os.Stderr, "GetMessage requires 1 args") 282 | flag.Usage() 283 | } 284 | argvalue0 := flag.Arg(1) 285 | value0 := argvalue0 286 | fmt.Print(client.GetMessage(context.Background(), value0)) 287 | fmt.Print("\n") 288 | break 289 | case "": 290 | Usage() 291 | break 292 | default: 293 | fmt.Fprintln(os.Stderr, "Invalid function ", cmd) 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /common/conf/account.conf: -------------------------------------------------------------------------------- 1 | ;[dev] 2 | 3 | account_host=127.0.0.1 4 | account_port=4000 5 | 6 | account_aes_key=bb6c59d065966e6236fcc635f88f8543 7 | 8 | ;日志 9 | log_path=./logs/account.log 10 | log_level=debug 11 | 12 | 13 | ;mysql mycat或mysql配置 14 | mysql_addr=localhost:3306 15 | mysql_user=root 16 | mysql_password=root 17 | mysql_db=fish 18 | 19 | ;redis cluster 集群配置 20 | redis_addrs=127.0.0.1:7000;127.0.0.1:7001;127.0.0.1:7002;127.0.0.1:8000;127.0.0.1:8001;127.0.0.1:8002 21 | redis_key_prefix=fish_ -------------------------------------------------------------------------------- /common/conf/game.conf: -------------------------------------------------------------------------------- 1 | ;[dev] 2 | 3 | game_host=127.0.0.1 4 | game_port=4002 5 | 6 | ;日志 7 | log_path=./logs/game.log 8 | log_level=debug 9 | -------------------------------------------------------------------------------- /common/conf/hall.conf: -------------------------------------------------------------------------------- 1 | ;[dev] 2 | 3 | hall_host=127.0.0.1 4 | hall_port=9000 5 | hall_secret=7Cw2ALPkht676IUB 6 | 7 | ;日志 8 | log_path=./logs/hall.log 9 | log_level=debug 10 | 11 | version=20190101 12 | 13 | app_id=01673379 14 | app_key=c18b1b56f2f88ef423bfeadbad9a816c 15 | redirect_uri=http://fish.blzz.shop/qq/message -------------------------------------------------------------------------------- /common/conf/traces.json: -------------------------------------------------------------------------------- 1 | { 2 | "201": [[[-548, 1002], [1914, -390]], [[-548, 1002], [1914, -312]], [[-548, 1002], [1914, -234]], [[-548, 1080], [1914, -390]], [[-548, 1080], [1914, -312]], [[-548, 1080], [1914, -234]]], 3 | "202": [[[-548, 1158], [1914, -390]], [[-548, 1158], [1914, -312]], [[-548, 1158], [1914, -234]]], 4 | "203": [[[1914, -390], [-548, 1002]], [[1914, -390], [-548, 1080]], [[1914, -390], [-548, 1158]]], 5 | "204": [[[1914, -312], [-548, 1002]], [[1914, -312], [-548, 1080]], [[1914, -312], [-548, 1158]]], 6 | "205": [[[1914, -234], [-548, 1002]], [[1914, -234], [-548, 1080]], [[1914, -234], [-548, 1158]]], 7 | "206": [[[-548, 362], [1914, 412]], [[-548, 462], [1914, 384]], [[-548, 462], [1914, 306]]], 8 | "207": [[[-548, 384], [1914, 462]], [[-548, 304], [1914, 384]], [[-548, 384], [1914, 306]]], 9 | "208": [[[-548, 406], [1914, 462]], [[-548, 326], [1914, 384]], [[-548, 306], [1914, 366]]], 10 | "209": [[[1914, 462], [-548, 462]], [[1914, 462], [-548, 384]], [[1914, 462], [-548, 306]]], 11 | "210": [[[1914, 384], [-548, 462]], [[1914, 384], [-548, 384]], [[1914, 384], [-548, 306]]], 12 | "211": [[[1914, 306], [-548, 462]], [[1914, 306], [-548, 384]], [[1914, 306], [-548, 306]]], 13 | "212": [[[-548, -390], [1914, 1002]], [[-548, -390], [1914, 1080]], [[-548, -624], [1914, 1158]]], 14 | "213": [[[-548, -312], [1914, 1002]], [[-548, -312], [1914, 1080]], [[-548, -312], [1914, 1158]]], 15 | "214": [[[-548, -234], [1914, 1002]], [[-548, -234], [1914, 1080]], [[-548, -234], [1914, 1158]]], 16 | "215": [[[1914, 1002], [-548, -390]], [[1914, 1002], [-548, -312]], [[1914, 1002], [-548, -234]]], 17 | "216": [[[1914, 1080], [-548, -390]], [[1914, 1080], [-548, -312]], [[1914, 1080], [-548, -234]]], 18 | "217": [[[1914, 1158], [-548, -390]], [[1914, 1158], [-548, -312]], [[1914, 1158], [-548, -234]]], 19 | "1": [[[10, -500], [0, 768], [2000, 1268]], [[50, -500], [0, 768], [2000, 1268]], [[210, -500], [0, 768], [2000, 1268]], [[250, -500], [0, 768], [2000, 1268]]], 20 | "2": [[[2000, -500], [0, 768], [10, 1268]], [[2100, -500], [0, 768], [10, 1268]], [[2150, -500], [0, 768], [10, 1268]], [[2200, -500], [0, 768], [10, 1268]]], 21 | "3": [[[-500, 10], [0, 768], [1866, 384]], [[-500, 110], [0, 768], [1866, 484]], [[-500, 210], [0, 768], [1866, 584]], [[-500, 310], [0, 68], [1866, 684]], [[-500, 410], [0, 768], [1866, 784]]], 22 | "4": [[[10, 1268], [0, 0], [2083, -500]], [[30, 1268], [0, 0], [2083, -500]]], 23 | "5": [[[70, 1268], [0, 0], [1783, -500]], [[110, 1268], [0, 0], [1783, -500]], [[170, 1268], [0, 0], [1783, -500]], [[210, 1268], [0, 0], [1783, -500]]], 24 | "6": [[[610, 1268], [0, 0], [1283, -500]], [[710, 1268], [0, 0], [1383, -500]]], 25 | "7": [[[1866, 10], [1366, 768], [-500, 384]], [[1866, 110], [1366, 768], [-500, 484]], [[1866, 210], [1366, 768], [-500, 584]], [[1866, 310], [1366, 768], [-500, 684]], [[1866, 410], [1366, 768], [-500, 784]]], 26 | "8": [[[60, 1268], [1366, 384], [60, -500]], [[160, 1268], [1366, 384], [160, -500]], [[260, 1268], [1366, 384], [260, -500]], [[360, 1268], [1366, 384], [360, -500]], [[460, 1268], [1366, 384], [460, -500]], [[560, 1268], [1366, 384], [560, -500]], [[660, 1268], [1366, 384], [660, -500]], [[860, 1268], [0, 384], [860, -500]], [[960, 1268], [0, 384], [960, -500]]], 27 | "9": [[[1060, 1268], [0, 384], [1060, -500]], [[1160, 1268], [0, 384], [1160, -500]], [[1260, 1268], [0, 384], [1260, -500]]], 28 | "10": [[[-500, 60], [683, 768], [1866, 60]], [[-500, 160], [683, 768], [1866, 160]], [[-500, 260], [683, 768], [1866, 260]], [[-500, 360], [683, 768], [1866, 360]], [[-500, 460], [683, 0], [1866, 460]], [[-500, 560], [683, 0], [1866, 560]], [[-500, 660], [683, 0], [1866, 660]], [[-500, 760], [683, 0], [1866, 760]]], 29 | "101": [[[-100, 384], [800, 1384], [566, -616], [1466, 384]], [[1466, 384], [566, -616], [800, 1384], [-100, 384]]], 30 | "102": [[[683, 868], [1866, 284], [-500, 484], [683, -100]], [[683, -100], [-500, 484], [1866, 284], [683, 868]]], 31 | "103": [[[-68, -38], [0, 1368], [1366, -600], [1434, 806]], [[1434, 806], [1366, -600], [0, 1368], [-68, -38]]], 32 | "104": [[[-68, -238], [0, 1168], [1366, -800], [1434, 606]], [[1366, 606], [1366, -800], [0, 1168], [-68, -238]]], 33 | "105": [[[483, 868], [1666, 284], [-700, 484], [483, -100]], [[483, -100], [-700, 484], [1666, 284], [483, 868]]], 34 | "106": [[[-100, 284], [800, 1284], [566, -716], [1466, 284]], [[1466, 284], [566, -716], [800, 1284], [-100, 284]]], 35 | "107": [[[-68, 162], [0, 1568], [1366, -400], [1434, 1006]], [[1434, 1006], [1366, -400], [0, 1568], [-68, 162]]], 36 | "108": [[[883, 868], [2066, 284], [-300, 484], [883, -100]], [[883, -100], [-300, 484], [2066, 284], [883, 868]]], 37 | "109": [[[-100, 484], [800, 1484], [566, -516], [1466, 484]], [[1466, 484], [566, -516], [800, 1484], [-100, 484]]], 38 | "110": [[[-68, -38], [1200, 1368], [166, -600], [1434, 806]], [[1434, 851], [166, -600], [1200, 1368], [-68, -38]]] 39 | } -------------------------------------------------------------------------------- /common/tools/aes.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "fmt" 7 | "encoding/hex" 8 | ) 9 | 10 | func NewAesTool(appSecret string) (aesTool *AesEncrypt,err error) { 11 | if len(appSecret) <16 { 12 | err = fmt.Errorf("invalid param appsecret of %s",appSecret) 13 | return 14 | } 15 | aesTool = &AesEncrypt{AppSecret:appSecret} 16 | return 17 | } 18 | 19 | type AesEncrypt struct { 20 | AppSecret string 21 | } 22 | 23 | func (p *AesEncrypt) getKey() []byte { 24 | strKey := p.AppSecret 25 | keyLen := len(strKey) 26 | if keyLen < 16 { 27 | panic("res key 长度不能小于16") 28 | } 29 | arrKey := []byte(strKey) 30 | if keyLen >= 32 { 31 | //取前32个字节 32 | return arrKey[:32] 33 | } 34 | if keyLen >= 24 { 35 | //取前24个字节 36 | return arrKey[:24] 37 | } 38 | //取前16个字节 39 | return arrKey[:16] 40 | } 41 | 42 | //加密字符串 43 | func (p *AesEncrypt) Encrypt(strMesg string) (string, error) { 44 | key := p.getKey() 45 | var iv = []byte(key)[:aes.BlockSize] 46 | encrypted := make([]byte, len(strMesg)) 47 | aesBlockEncrypter, err := aes.NewCipher(key) 48 | if err != nil { 49 | return "", err 50 | } 51 | aesEncrypter := cipher.NewCFBEncrypter(aesBlockEncrypter, iv) 52 | aesEncrypter.XORKeyStream(encrypted, []byte(strMesg)) 53 | encodeString := fmt.Sprintf("%x",encrypted) 54 | //encodeString := base64.StdEncoding.EncodeToString([]byte(encrypted)) 55 | return encodeString, nil 56 | } 57 | 58 | //解密字符串 59 | func (p *AesEncrypt) Decrypt(aesEncryptString string) (strDesc string, err error) { 60 | src, err := hex.DecodeString(aesEncryptString) 61 | //src, err := base64.StdEncoding.DecodeString(aesEncryptString) 62 | if err != nil { 63 | return 64 | } 65 | defer func() { 66 | //错误处理 67 | if e := recover(); e != nil { 68 | err = e.(error) 69 | } 70 | }() 71 | key := p.getKey() 72 | var iv = []byte(key)[:aes.BlockSize] 73 | decrypted := make([]byte, len(src)) 74 | var aesBlockDecrypter cipher.Block 75 | aesBlockDecrypter, err = aes.NewCipher([]byte(key)) 76 | if err != nil { 77 | return "", err 78 | } 79 | aesDecrypter := cipher.NewCFBDecrypter(aesBlockDecrypter, iv) 80 | aesDecrypter.XORKeyStream(decrypted, src) 81 | return string(decrypted), nil 82 | } -------------------------------------------------------------------------------- /common/tools/call_rpc.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fish/common/api/thrift/gen-go/rpc" 5 | "fmt" 6 | "github.com/apache/thrift/lib/go/thrift" 7 | "net" 8 | ) 9 | 10 | func GetRpcClient(host,port string) (client *rpc.UserServiceClient, closeTransport func() error, err error) { 11 | var transport thrift.TTransport 12 | transport, err = thrift.NewTSocket(net.JoinHostPort(host,port)) 13 | if err != nil { 14 | err = fmt.Errorf("NewTSocket failed. err: [%v]\n", err) 15 | return 16 | } 17 | 18 | transport, err = thrift.NewTBufferedTransportFactory(8192).GetTransport(transport) 19 | if err != nil { 20 | err = fmt.Errorf("NewTransport failed. err: [%v]\n", err) 21 | return 22 | } 23 | closeTransport = transport.Close 24 | 25 | if err = transport.Open(); err != nil { 26 | err = fmt.Errorf("Transport.Open failed. err: [%v]\n", err) 27 | return 28 | } 29 | protocolFactory := thrift.NewTCompactProtocolFactory() 30 | iprot := protocolFactory.GetProtocol(transport) 31 | oprot := protocolFactory.GetProtocol(transport) 32 | client = rpc.NewUserServiceClient(thrift.NewTStandardClient(iprot, oprot)) 33 | return 34 | } -------------------------------------------------------------------------------- /common/tools/snowFlake.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | nodeBits uint8 = 10 11 | stepBits uint8 = 12 12 | nodeMax int64 = -1 ^ (-1 << nodeBits) 13 | stepMax int64 = -1 ^ (-1 << stepBits) 14 | timeShift uint8 = nodeBits + stepBits 15 | nodeShift uint8 = stepBits 16 | ) 17 | 18 | var ( 19 | Epoch int64 = 1546272000000 20 | ) 21 | 22 | type Node struct { 23 | mu sync.Mutex 24 | timestamp int64 25 | node int64 26 | step int64 27 | GenerateChan chan int64 28 | stop chan bool 29 | } 30 | 31 | func GenerateUid(nodeNum int64) (err error, generateChan <-chan int64) { 32 | var node *Node 33 | if node, err = NewNode(nodeNum);err == nil { 34 | generateChan = node.GenerateChan 35 | go node.run() 36 | return 37 | } 38 | 39 | return 40 | } 41 | 42 | func (n *Node) run() { 43 | for { 44 | n.GenerateChan <- n.Generate() 45 | } 46 | } 47 | 48 | func NewNode(nodeNum int64) (*Node, error) { 49 | if nodeNum < 0 || nodeNum > nodeMax { 50 | return nil, errors.New("Node number must be between 0 and 1023") 51 | } 52 | return &Node{ 53 | timestamp: 0, 54 | node: nodeNum, 55 | step: 0, 56 | GenerateChan: make(chan int64), 57 | stop: make(chan bool), 58 | }, nil 59 | } 60 | 61 | func (n *Node) Generate() int64 { 62 | n.mu.Lock() 63 | defer n.mu.Unlock() 64 | now := time.Now().UnixNano() / 1e6 65 | if n.timestamp == now { 66 | n.step++ 67 | if n.step > stepMax { 68 | for now <= n.timestamp { 69 | now = time.Now().UnixNano() / 1e6 70 | } 71 | n.step = 0 72 | } 73 | } else { 74 | n.timestamp = now 75 | n.step = 0 76 | } 77 | return (now-Epoch)<= FishKind23 && fish.FishKind <= FishKind26 { //一网打尽 267 | killedFishes = c.Room.getAllInOne(fish) 268 | } else if fish.FishKind >= FishKind31 && fish.FishKind <= FishKind33 { 269 | killedFishes = c.Room.getSameFish(fish) 270 | } 271 | //加钱 272 | addScore := 0 273 | for _, fish := range killedFishes { 274 | addScore += GetFishMulti(fish) * GetBulletMulti(bullet.BulletKind) * c.Room.Conf.BaseScore 275 | } 276 | //if addScore > c.Room.Conf.BaseScore*200 { //不允许超过200倍 277 | // logs.Error("user %v catch fish kind [%v] add score = %v,base score = %v ,beyond 200 time of base score...", c.UserInfo.UserId, fish.FishKind, addScore, c.Room.Conf.BaseScore) 278 | // addScore = c.Room.Conf.BaseScore * 200 279 | //} 280 | c.UserInfo.Score += addScore 281 | c.UserInfo.Bill += addScore //记账 282 | //todo %1的概率获取冰冻道具 283 | rand.Seed(time.Now().UnixNano()) 284 | item := "" 285 | if rand.Intn(100) == 0 { 286 | item = "ice" 287 | } 288 | fishes := make([]string, 0) 289 | for _, fish := range killedFishes { 290 | fishes = append(fishes, strconv.Itoa(int(fish.FishId))) 291 | } 292 | catchFishAddScore, _ := strconv.ParseFloat(fmt.Sprintf("%.5f", float64(addScore)/1000), 64) 293 | catchResult := []interface{}{"catch_fish_reply", 294 | map[string]interface{}{ 295 | "userId": c.UserInfo.UserId, 296 | "chairId": bullet.ChairId, 297 | "bulletId": bullet.BulletId, 298 | "fishId": strings.Join(fishes, ","), 299 | "addScore": catchFishAddScore, 300 | "item": item, 301 | }} 302 | c.Room.broadcast(catchResult) 303 | //logs.Debug("catch fish add score %v,catchFishAddScore %v", addScore, catchFishAddScore) 304 | for _, fish := range killedFishes { 305 | delete(c.Room.AliveFish, fish.FishId) 306 | } 307 | } else { 308 | //logs.Debug("hit fish failed...") 309 | } 310 | } else { 311 | logs.Debug("user [%v] catch fish fishId [%v] not in alive fish array...", c.UserInfo.UserId, fishId) 312 | } 313 | } else { 314 | logs.Debug("user [%v] catch fish bullet [%v] belong to user [%v] ...", c.UserInfo.UserId, bullet.BulletId, bullet.UserId) 315 | } 316 | delete(c.Room.AliveBullet, bulletId) 317 | } else { 318 | //客户端会多传命中,愚蠢的客户端 319 | //logs.Debug("user [%v] catch fish bullet [%v] not exists ...", c.UserInfo.UserId, bulletId) 320 | } 321 | } 322 | 323 | func (c *Client) frozenScene(startTime time.Time) { //冰冻屏幕 324 | if c.Room.FrozenEndTime.Unix() > time.Now().Unix() { 325 | return 326 | } 327 | logs.Debug("frozenScene") 328 | c.Room.Status = GameStatusFrozen 329 | c.Room.Utils.StopBuildFish <- true 330 | c.Room.FrozenEndTime = startTime.Add(time.Second * 10) 331 | cutDown := c.Room.FrozenEndTime.Sub(time.Now()) 332 | replyData := []interface{}{"user_frozen_reply", map[string]time.Duration{"cutDownTime": cutDown / 1e6}} 333 | c.sendToOthers(replyData) 334 | c.Room.frozenEndTimer = time.After(cutDown) 335 | } 336 | 337 | func (c *Client) exitRoom() { 338 | delete(c.Room.Users, c.UserInfo.UserId) 339 | //todo 持久化结算 340 | } 341 | 342 | func (c *Client) clearBill() { 343 | go func(userId UserId, bill int, roomId RoomId, power float64) { 344 | if client, closeTransportHandler, err := tools.GetRpcClient(common.GameConf.AccountHost, strconv.Itoa(common.GameConf.AccountPort)); err == nil { 345 | defer func() { 346 | if err := closeTransportHandler(); err != nil { 347 | logs.Error("close rpc err: %v", err) 348 | } 349 | }() 350 | if res, err := client.ModifyUserInfoById(context.Background(), "FISH_GAME_MODIFY", int32(userId), rpc.ModifyPropType_gems, int64(bill)); err == nil { 351 | if res.Code == rpc.ErrorCode_Success { 352 | logs.Debug("user [%v] clear bill success :in room [%v] fish game , add score : %v", userId, roomId, int64(bill)) 353 | } else { 354 | logs.Debug("user [%v] clear bill failed :in room [%v] fish game , add score : %v,err code = %v", userId, roomId, int64(bill), res.Code) 355 | } 356 | } else { 357 | logs.Error("user [%v] clearBill [%v] err: %v", userId, bill, err) 358 | } 359 | if res, err := client.ModifyUserInfoById(context.Background(), "FISH_GAME_MODIFY", int32(userId), rpc.ModifyPropType_power, int64(power)); err == nil { 360 | if res.Code == rpc.ErrorCode_Success { 361 | logs.Debug("user [%v] clear power success :in room [%v] fish game , add power : %v", userId, roomId, int64(power)) 362 | } else { 363 | logs.Debug("user [%v] clear power failed :in room [%v] fish game , add power : %v,err code = %v", userId, roomId, int64(power), res.Code) 364 | } 365 | } else { 366 | logs.Error("user [%v] clearBill [%v] err: %v", userId, bill, err) 367 | } 368 | } 369 | }(c.UserInfo.UserId, c.UserInfo.Bill, c.Room.RoomId, c.UserInfo.Power*1000) 370 | // 不允许其他协程修改client,默认结算成功。如果需要确认,可以在房间加结算消息chan。 371 | c.UserInfo.Bill = 0 372 | } 373 | -------------------------------------------------------------------------------- /game/service/define.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | /* 12 | // 座位号 13 | ------------- 14 | 0 1 2 15 | 7 3 16 | 6 5 4 17 | ------------- 18 | */ 19 | 20 | var ( 21 | SwitchSceneTimer = 60 * 5 // 5分钟切一次场景 22 | 23 | //鱼阵场景 24 | SceneKind1 = 0 25 | SceneKind2 = 1 26 | SceneKind3 = 2 27 | SceneKind4 = 3 28 | SceneKind5 = 4 29 | SceneKind6 = 5 30 | SceneKind7 = 6 31 | SceneKind8 = 7 32 | 33 | //鱼的种类 34 | FishKind1 = 1 35 | FishKind2 = 2 36 | FishKind3 = 3 37 | FishKind4 = 4 38 | FishKind5 = 5 39 | FishKind6 = 6 40 | FishKind7 = 7 41 | FishKind8 = 8 42 | FishKind9 = 9 43 | FishKind10 = 10 44 | FishKind11 = 11 45 | FishKind12 = 12 46 | FishKind13 = 13 47 | FishKind14 = 14 48 | FishKind15 = 15 49 | FishKind16 = 16 50 | FishKind17 = 17 51 | FishKind18 = 18 52 | FishKind19 = 19 53 | FishKind20 = 20 54 | FishKind21 = 21 55 | FishKind22 = 22 56 | FishKind23 = 23 // 一网打尽 57 | FishKind24 = 24 // 一网打尽 58 | FishKind25 = 25 // 一网打尽 59 | FishKind26 = 26 // 一网打尽 60 | FishKind27 = 27 61 | FishKind28 = 28 62 | FishKind29 = 29 63 | FishKind30 = 30 // 全屏炸弹 64 | FishKind31 = 31 // 同类炸弹 65 | FishKind32 = 32 // 同类炸弹 66 | FishKind33 = 33 // 同类炸弹 67 | FishKind34 = 34 68 | FishKind35 = 35 69 | 70 | FishMulti = map[int]int{ 71 | 1: 2, 72 | 2: 2, 73 | 3: 3, 74 | 4: 4, 75 | 5: 5, 76 | 6: 5, 77 | 7: 6, 78 | 8: 7, 79 | 9: 8, 80 | 10: 9, 81 | 11: 10, 82 | 12: 11, 83 | 13: 12, 84 | 14: 18, 85 | 15: 25, 86 | 16: 30, 87 | 17: 35, 88 | 18: 40, 89 | 19: 45, 90 | 20: 50, 91 | 21: 80, 92 | 22: 100, 93 | 23: 45, //45-150, // 一网打尽 94 | 24: 45, //45-150, // 一网打尽 95 | 25: 45, //45-150, // 一网打尽 96 | 26: 45, //45-150, // 一网打尽 97 | 27: 50, 98 | 28: 60, 99 | 29: 70, 100 | 30: 100, // 全屏炸弹 101 | 31: 110, // 同类炸弹 102 | 32: 110, // 同类炸弹 103 | 33: 110, // 同类炸弹 104 | 34: 120, 105 | 35: 200, 106 | } 107 | 108 | BulletKind = map[string]int{ 109 | "bullet_kind_normal_1": 0, 110 | "bullet_kind_normal_2": 1, 111 | "bullet_kind_normal_3": 2, 112 | "bullet_kind_vip1_1": 3, 113 | "bullet_kind_vip1_2": 4, 114 | "bullet_kind_vip1_3": 5, 115 | "bullet_kind_vip2_1": 6, 116 | "bullet_kind_vip2_2": 7, 117 | "bullet_kind_vip2_3": 8, 118 | "bullet_kind_vip3_1": 9, 119 | "bullet_kind_vip3_2": 10, 120 | "bullet_kind_vip3_3": 11, 121 | "bullet_kind_vip4_1": 12, 122 | "bullet_kind_vip4_2": 13, 123 | "bullet_kind_vip4_3": 14, 124 | "bullet_kind_vip5_1": 15, 125 | "bullet_kind_vip5_2": 16, 126 | "bullet_kind_vip5_3": 17, 127 | "bullet_kind_vip6_1": 19, 128 | "bullet_kind_vip6_2": 20, 129 | "bullet_kind_vip6_3": 21, 130 | "bullet_kind_laser": 22, 131 | } 132 | 133 | BulletMulti = map[int]int{ 134 | 1: 1, 135 | 2: 2, 136 | 3: 3, 137 | 4: 1, 138 | 5: 3, 139 | 6: 5, 140 | 7: 1, 141 | 8: 3, 142 | 9: 5, 143 | 10: 1, 144 | 11: 3, 145 | 12: 5, 146 | 13: 1, 147 | 14: 3, 148 | 15: 5, 149 | 16: 1, 150 | 17: 3, 151 | 18: 5, 152 | 19: 1, 153 | 20: 3, 154 | 21: 5, 155 | 22: 1, // 激光炮 156 | } 157 | ) 158 | 159 | const ( 160 | //SUB_S_GAME_CONFIG = "SUB_S_GAME_CONFIG" 161 | //SUB_S_FISH_TRACE = "SUB_S_FISH_TRACE" 162 | //SUB_S_EXCHANGE_FISHSCORE = "SUB_S_FISH_TRACE" 163 | //SUB_S_USER_FIRE = "SUB_S_FISH_TRACE" 164 | //SUB_S_CATCH_FISH = "SUB_S_FISH_TRACE" 165 | //SUB_S_BULLET_ION_TIMEOUT = "SUB_S_BULLET_ION_TIMEOUT" 166 | //SUB_S_LOCK_TIMEOUT = "SUB_S_LOCK_TIMEOUT" 167 | //SUB_S_CATCH_SWEEP_FISH = "SUB_S_CATCH_SWEEP_FISH" 168 | //SUB_S_CATCH_SWEEP_FISH_RESULT = "SUB_S_CATCH_SWEEP_FISH_RESULT" 169 | //SUB_S_HIT_FISH_LK = "SUB_S_HIT_FISH_LK" 170 | //SUB_S_SWITCH_SCENE = "SUB_S_SWITCH_SCENE" 171 | //SUB_S_STOCK_OPERATE_RESULT = 111 //库存操作 172 | //SUB_S_SCENE_END = 112 //场景结束 173 | //SUB_S_CATCH_FISHRESULT = 113 //捉鱼结果 174 | //SUB_S_SETTLE_FISHSCORE = 114 //解决鱼分数 175 | //SUB_S_SWIM_SCENE = 115 //游泳场景 176 | //SUB_S_SPECIAL_PRICE1 = 116 //特价1 177 | //SUB_S_ADD_PRICE1_SCORE = 117 //添加价格分数 178 | //SUB_S_END_SPECIAL1 = 118 //结束特别 179 | //SUB_S_SPECIAL_PRICE2 = 119 180 | //SUB_S_UPDATE_POS = 120 181 | //SUB_S_END_SPECIAL2 = 121 182 | //SUB_S_SPECIAL_PRICE3 = 122 183 | //SUB_S_END_SPECIAL3 = 123 184 | //SUB_S_LOCK_FISH = 124 //锁定鱼 185 | //SUB_S_BLACK_LIST = 125 //黑名单 186 | //SUB_S_WHITE_LIST = 126 //白名单 187 | //SUB_S_BIGFISH_LIST = 127 //大鱼名单 188 | //SUB_S_LINE_TRACE = 128 //线追踪 189 | //SUB_S_SHOAL_TRACE = 129 //浅追踪 190 | 191 | //基础分值,底分 192 | GameBaseScore = 1 193 | //最小携带金币 194 | MinHaveScore = 1 195 | //最大携带金币 196 | MaxHaveScore = 100 197 | //抽水比例,千分比,5代表千分之5 198 | TaxRatio = 5 199 | ) 200 | 201 | var pathMap = make(map[string][][][]int) 202 | 203 | func LoadTraceFile(path string) (err error) { 204 | _, err = os.Stat(path) 205 | if os.IsNotExist(err) { 206 | return fmt.Errorf("file %v not exists", path) 207 | } 208 | file, err := os.Open(path) 209 | if err != nil { 210 | panic(err) 211 | } 212 | defer file.Close() 213 | 214 | var jsonStrByte []byte 215 | for { 216 | buf := make([]byte, 1024) 217 | readNum, err := file.Read(buf) 218 | if err != nil && err != io.EOF { 219 | panic(err) 220 | } 221 | for i := 0; i < readNum; i++ { 222 | jsonStrByte = append(jsonStrByte, buf[i]) 223 | } 224 | if 0 == readNum { 225 | break 226 | } 227 | } 228 | err = json.Unmarshal(jsonStrByte, &pathMap) 229 | if err != nil { 230 | fmt.Printf("json unmarsha1 err:%v \n", err) 231 | return 232 | } else { 233 | fmt.Println("success") 234 | } 235 | return 236 | } 237 | 238 | func getPathMap(id int) [][][]int { 239 | return pathMap[strconv.Itoa(id)] 240 | } 241 | -------------------------------------------------------------------------------- /game/service/fish_utils.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/astaxie/beego/logs" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type FishUtil struct { 10 | //ActiveFish []*Fish 11 | 12 | //Lock sync.Mutex 13 | CurrentFishId FishId 14 | BuildFishChan chan *Fish 15 | StopBuildFish chan bool //暂停出鱼 16 | RestartBuildFish chan bool //重新开始出鱼 17 | Exit chan bool //接收信号 18 | } 19 | type FishId int 20 | 21 | //普通鱼 22 | type Fish struct { 23 | FishKind int `json:"fishKind"` 24 | Trace [][]int `json:"trace"` 25 | Speed int `json:"speed"` 26 | FishId FishId `json:"fishId"` 27 | ActiveTime time.Time `json:"-"` 28 | FrontActiveTime int64 `json:"activeTime"` //给客户端的时间 29 | } 30 | 31 | //组合鱼 32 | type ArrayFish struct { 33 | FishKind int `json:"fishKind"` 34 | TraceKind int `json:"traceKind"` 35 | FishId FishId `json:"fishId"` 36 | Speed int `json:"speed"` 37 | } 38 | 39 | type FishArrayRet struct { 40 | FormationKind int `json:"formationKind"` 41 | FishArray [][]*ArrayFish `json:"fishArray"` 42 | EndTime time.Time `json:"-"` 43 | EndTimeStamp int64 `json:"endTime"` 44 | } 45 | 46 | func (p *FishUtil) GenerateFishId() FishId { 47 | p.CurrentFishId++ 48 | return p.CurrentFishId 49 | } 50 | 51 | func (p *FishUtil) BuildFishTrace() { 52 | var buildTrace = func() int { 53 | // 线路随机生成 54 | var traceId = 101 55 | //var traceRandom = Math.floor(Math.random() * 1000) + 1 56 | rand.Seed(time.Now().UnixNano()) 57 | var traceKind = rand.Int() 58 | randNum := rand.Int() 59 | switch traceKind%3 + 1 { 60 | case 1: // 直线 201-217 61 | traceId = randNum%17 + 201 62 | break 63 | case 2: // 二阶曲线 1-10 64 | traceId = randNum%10 + 1 65 | break 66 | case 3: // 三阶曲线 101 -110 67 | traceId = randNum%10 + 101 68 | break 69 | } 70 | return traceId 71 | } 72 | 73 | c1 := time.NewTicker(time.Second * 2) 74 | c2 := time.NewTicker(time.Second*10 + time.Millisecond*100) 75 | c3 := time.NewTicker(time.Second*30 + time.Millisecond*200) 76 | c4 := time.NewTicker(time.Second * 61) 77 | go func() { 78 | defer func() { 79 | logs.Trace("exit utils") 80 | }() 81 | defer func() { 82 | c1.Stop() 83 | c2.Stop() 84 | c3.Stop() 85 | c4.Stop() 86 | close(p.BuildFishChan) 87 | close(p.StopBuildFish) 88 | close(p.RestartBuildFish) 89 | }() 90 | //logs.Debug("utils start running ...") 91 | var buildNormalFish = func() { 92 | rand.Seed(time.Now().UnixNano()) 93 | traceKind := buildTrace() 94 | fishKind := rand.Intn(15) + 1 95 | traces := getPathMap(traceKind) 96 | 97 | //logs.Debug("add normal fish tick run") 98 | for i := 0; i < len(traces); i++ { 99 | fishId := p.GenerateFishId() 100 | p.AddFish(fishKind, traces[i], fishId) 101 | } 102 | } 103 | buildNormalFish() 104 | for { 105 | //logs.Error("for loop......") 106 | select { 107 | case <-c1.C: //随机生成鱼 1-15 108 | buildNormalFish() 109 | case <-c2.C: // 16-20 110 | //logs.Error("<-c2.C in") 111 | rand.Seed(time.Now().UnixNano()) 112 | fishKind := rand.Intn(5) + 16 113 | fishId := p.GenerateFishId() 114 | traceKind := buildTrace() 115 | traces := getPathMap(traceKind) 116 | p.AddFish(fishKind, traces[0], fishId) 117 | case <-c3.C: // 21-34 118 | //logs.Error("<-c3.C in") 119 | rand.Seed(time.Now().UnixNano()) 120 | fishKind := rand.Intn(14) + 21 121 | fishId := p.GenerateFishId() 122 | traceKind := buildTrace() 123 | traces := getPathMap(traceKind) 124 | p.AddFish(fishKind, traces[1], fishId) 125 | 126 | case <-c4.C: // 鱼王 127 | //logs.Error("<-c4.C in") 128 | fishKind := 35 129 | fishId := p.GenerateFishId() 130 | rand.Seed(time.Now().UnixNano()) 131 | traceKind := rand.Intn(10) + 101 132 | traces := getPathMap(traceKind) 133 | p.AddFish(fishKind, traces[1], fishId) 134 | case <-p.StopBuildFish: //停止出鱼 135 | logs.Trace("build util StopBuildFish...") 136 | c1.Stop() 137 | c2.Stop() 138 | c3.Stop() 139 | c4.Stop() 140 | //logs.Debug("<-p.StopBuildFish") 141 | case <-p.RestartBuildFish: 142 | logs.Trace("build util RestartBuildFish...") 143 | c1 = time.NewTicker(time.Second * 2) 144 | c2 = time.NewTicker(time.Second*10 + time.Millisecond*100) 145 | c3 = time.NewTicker(time.Second*30 + time.Millisecond*200) 146 | c4 = time.NewTicker(time.Second * 61) 147 | //logs.Debug("<-p.RestartBuildFish") 148 | //return 149 | case <-p.Exit: 150 | //logs.Error("<-p.Exit") 151 | //退出关闭资源 152 | //close(p.StopBuildFish) 153 | //close(p.RestartBuildFish) 154 | close(p.Exit) 155 | //logs.Debug("<-p.Exit") 156 | return 157 | } 158 | } 159 | }() 160 | } 161 | 162 | func (p *FishUtil) AddFish(fishKind int, trace [][]int, fishId FishId) { 163 | 164 | var speed = 6 165 | if fishId >= 35 { 166 | speed = 3 167 | } else if fishId >= 30 { 168 | speed = 4 169 | } else if fishId >= 20 { 170 | speed = 5 171 | } 172 | p.BuildFishChan <- &Fish{ 173 | FishKind: fishKind, 174 | Trace: trace, 175 | Speed: speed, 176 | FishId: fishId, 177 | ActiveTime: time.Now(), 178 | FrontActiveTime: time.Now().UnixNano() / 1e6, 179 | } 180 | } 181 | 182 | //启动鱼阵 183 | func BuildFishArray() (ret *FishArrayRet) { 184 | var fishId FishId 185 | var generateFishId = func() FishId { 186 | fishId++ 187 | return fishId 188 | } 189 | var fishArray = make([][]*ArrayFish, 0) 190 | var duration = 0 191 | //直线鱼阵 192 | var buildFormationLine = func() { 193 | duration = 60 194 | fishArray = append(fishArray, make([]*ArrayFish, 0)) 195 | fishArray = append(fishArray, make([]*ArrayFish, 0)) 196 | var kind = 14 197 | for i := 0; i < 30; i++ { 198 | kind = i/3 + 10 199 | fishArray[0] = append(fishArray[0], &ArrayFish{ 200 | FishKind: kind, 201 | TraceKind: 0, 202 | FishId: generateFishId(), 203 | Speed: 0, 204 | }) 205 | fishArray[1] = append(fishArray[1], &ArrayFish{ 206 | FishKind: kind, 207 | TraceKind: 0, 208 | FishId: generateFishId(), 209 | Speed: 0, 210 | }) 211 | } 212 | } 213 | 214 | //环形鱼阵 215 | var buildCircleGroupFish = func() { 216 | duration = 60 217 | kind, fishNum := 1, 20 218 | for i := 0; i < 10; i++ { 219 | kind += 2 220 | fishArray = append(fishArray, make([]*ArrayFish, 0)) 221 | if i > 20 { 222 | fishNum = 10 223 | } 224 | for j := 0; j < fishNum; j++ { 225 | fishArray[i] = append(fishArray[i], &ArrayFish{ 226 | FishKind: kind, 227 | TraceKind: 0, 228 | FishId: generateFishId(), 229 | Speed: 0, 230 | }) 231 | } 232 | } 233 | } 234 | 235 | // 两个螺旋形数组 236 | var buildSpiralGroupFish = func() { 237 | duration = 60 238 | fishArray = append(fishArray, make([]*ArrayFish, 0)) 239 | fishArray = append(fishArray, make([]*ArrayFish, 0)) 240 | kind := 1 241 | for i := 1; i <= 30; i++ { 242 | kind = ((i-1)/10 + 1) * 5 243 | fishArray[0] = append(fishArray[0],&ArrayFish{ 244 | FishKind: kind, 245 | TraceKind: 0, 246 | FishId: generateFishId(), 247 | Speed: 0, 248 | }) 249 | fishArray[1] = append(fishArray[1],&ArrayFish{ 250 | FishKind: kind, 251 | TraceKind: 0, 252 | FishId: generateFishId(), 253 | Speed: 0, 254 | }) 255 | } 256 | } 257 | ret = &FishArrayRet{} 258 | ret.FormationKind = rand.Intn(3) + 1 259 | //ret.FormationKind = 1 260 | switch ret.FormationKind { 261 | case 1: 262 | buildFormationLine() 263 | case 2: 264 | buildCircleGroupFish() 265 | case 3: 266 | buildSpiralGroupFish() 267 | } 268 | ret.FishArray = fishArray 269 | ret.EndTime = time.Now().Add(time.Second * time.Duration(duration)) 270 | ret.EndTimeStamp = ret.EndTime.Unix() * 1e3 271 | return 272 | } 273 | 274 | //是否命中 275 | func IsHit(f *Fish) bool { 276 | rand.Seed(time.Now().UnixNano()) 277 | // todo 调整概率 278 | return rand.Intn(GetFishMulti(f)) == 0 279 | //return rand.Intn(GetFishMulti(f)*3/5) == 0 280 | //return true 281 | } 282 | 283 | func GetFishMulti(fish *Fish) int { 284 | if multi, ok := FishMulti[fish.FishKind]; ok { 285 | return multi 286 | } else { 287 | return 2 288 | } 289 | } 290 | 291 | // 根据id取得子弹的倍数 292 | func GetBulletMulti(BulletKind int) int { 293 | if multi, ok := BulletMulti[BulletKind]; ok { 294 | return multi 295 | } else { 296 | return 1 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /game/service/request.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fish/game/common" 9 | "fmt" 10 | "github.com/astaxie/beego/logs" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | type UserLockFishReq struct { 17 | UserId UserId `json:"userId"` 18 | ChairId int `json:"chairId"` 19 | FishId FishId `json:"fishId"` 20 | } 21 | 22 | type LaserCatchReq struct { 23 | UserId UserId `json:"userId"` 24 | ChairId int `json:"chairId"` 25 | Fishes string `json:"fishes"` 26 | Sign string `json:"sign"` 27 | } 28 | 29 | type UserFireLaserReq struct { 30 | UserId UserId `json:"userId"` 31 | ChairId int `json:"chairId"` 32 | BulletKind int `json:"bulletKind"` 33 | BulletId int `json:"bulletId"` 34 | Angle float64 `json:"angle"` 35 | Sign string `json:"sign"` 36 | LockFishId FishId `json:"lockFishId"` 37 | } 38 | 39 | func wsRequest(req []byte, client *Client) { 40 | defer func() { 41 | if r := recover(); r != nil { 42 | logs.Error("wsRequest panic:%v ", r) 43 | } 44 | }() 45 | if req[0] == '4' && req[1] == '2' { 46 | reqJson := make([]string, 0) 47 | err := json.Unmarshal(req[2:], &reqJson) 48 | if err != nil { 49 | logs.Error("wsRequest json unmarshal err :%v", err) 50 | return 51 | } 52 | if client.Room == nil { //未登录 53 | logs.Info("未登录 login msg : %v", reqJson[0]) 54 | if reqJson[0] == "login" { 55 | if len(reqJson) < 2 { 56 | return 57 | } 58 | //if reqByteData, ok := reqJson[1].([]byte); ok { 59 | reqData := make(map[string]string) 60 | if err := json.Unmarshal([]byte(reqJson[1]), &reqData); err != nil { 61 | roomIdStr := reqData["roomId"] 62 | if roomIdStr == "" { //客户端重连时roomId用的int类型。。。心累 63 | reqDataReconnect := make(map[string]int) 64 | if err := json.Unmarshal([]byte(reqJson[1]), &reqDataReconnect); err != nil { 65 | roomIdInt := reqDataReconnect["roomId"] 66 | roomIdStr = strconv.Itoa(roomIdInt) 67 | } 68 | } 69 | if roomIdInt, err := strconv.Atoi(roomIdStr); err == nil { 70 | roomId := RoomId(roomIdInt) 71 | RoomMgr.RoomLock.Lock() 72 | logs.Info("login get lock...") 73 | defer RoomMgr.RoomLock.Unlock() 74 | defer logs.Info("login set free lock...") 75 | if room, ok := RoomMgr.Rooms[roomId]; ok { 76 | //if room.Status == GameStatusWaitBegin { 77 | // room.Status = GameStatusFree 78 | // room.Utils.BuildFishTrace() 79 | //} 80 | logs.Debug("send succ") 81 | client.Room = room 82 | room.ClientReqChan <- &clientReqData{ 83 | client, 84 | reqJson, 85 | } 86 | } else { 87 | logs.Error("room %v, not exists", roomId) 88 | } 89 | } else { 90 | logs.Error("roomId %v err : %v", roomIdStr, err) 91 | } 92 | } 93 | //} 94 | } else { 95 | logs.Error("invalid act %v", reqJson[0]) 96 | } 97 | } else { 98 | //logs.Debug("send req to room [%d] succ 2", client.Room.RoomId) 99 | client.Room.ClientReqChan <- &clientReqData{ 100 | client, 101 | reqJson, 102 | } 103 | } 104 | } else { 105 | logs.Error("invalid message %v", req) 106 | } 107 | } 108 | 109 | //todo 弱类型语言写的东西重构简直堪比火葬场 110 | func handleUserRequest(clientReq *clientReqData) { 111 | reqJson := clientReq.reqData 112 | client := clientReq.client 113 | if len(reqJson) > 0 { 114 | act := reqJson[0] 115 | switch act { 116 | case "login": 117 | //logs.Debug("login") 118 | if len(reqJson) < 2 { 119 | return 120 | } 121 | reqData := make(map[string]interface{}) 122 | if err := json.Unmarshal([]byte(reqJson[1]), &reqData); err == nil { 123 | token := reqData["sign"] 124 | if token, ok := token.(string); ok { 125 | logs.Debug("token %v", token) 126 | if rpcClient, closeTransportHandler, err := tools.GetRpcClient(common.GameConf.AccountHost, strconv.Itoa(common.GameConf.AccountPort)); err == nil { 127 | defer func() { 128 | if err := closeTransportHandler(); err != nil { 129 | logs.Error("close rpc err: %v", err) 130 | } 131 | }() 132 | if res, err := rpcClient.GetUserInfoByToken(context.Background(), token); err == nil { 133 | //logs.Debug("rpc res : %v", res.Code) 134 | if res.Code == rpc.ErrorCode_Success { 135 | userId := UserId(res.UserObj.UserId) 136 | for _, userInfo := range client.Room.Users { 137 | if userId == userInfo.UserId { 138 | userInfo.client = client 139 | userInfo.Online = true 140 | userInfo.Ip = "::1" 141 | client.UserInfo = userInfo 142 | logs.Debug("client userInfo get data...") 143 | seats := make([]interface{}, 0) 144 | cannonKindVip := map[int]int{0: 1, 1: 4, 2: 7, 3: 10, 4: 13, 5: 16, 6: 19} 145 | //todo check sign 146 | //score, _ := strconv.ParseFloat(fmt.Sprintf("%.3f", float64(userInfo.Score)/1000), 64) 147 | userInfo.ConversionScore, _ = strconv.ParseFloat(fmt.Sprintf("%.3f", float64(userInfo.Score)/1000), 64) 148 | for _, userInfo := range client.Room.Users { 149 | seats = append(seats, map[string]interface{}{ 150 | "userId": userInfo.UserId, 151 | "ip": "", 152 | "score": userInfo.ConversionScore, 153 | "name": userInfo.Name, 154 | "vip": userInfo.Vip, 155 | "online": true, 156 | "ready": userInfo.Ready, 157 | "seatIndex": userInfo.SeatIndex, 158 | 159 | // 正在使用哪种炮 todo 换为真实vip 160 | "cannonKind": cannonKindVip[0], 161 | // 能量值 162 | "power": 0, 163 | }) 164 | } 165 | client.sendToClient([]interface{}{ 166 | "login_result", 167 | map[string]interface{}{ 168 | "errcode": 0, 169 | "errmsg": "ok", 170 | "data": map[string]interface{}{ 171 | "roomId": strconv.Itoa(int(client.Room.RoomId)), 172 | "conf": client.Room.Conf, 173 | "numofgames": 0, 174 | "seats": seats, 175 | }, 176 | }, 177 | }) 178 | client.sendToOthers([]interface{}{ 179 | "new_user_comes_push", 180 | client.UserInfo, 181 | }) 182 | client.sendToClient([]interface{}{ 183 | "login_finished", 184 | }) 185 | return 186 | } 187 | } 188 | //不用断开链接,客户端的问题导致需要保持很多无用链接。。。 189 | logs.Debug("user need enter room") 190 | client.closeChan <- true 191 | close(client.closeChan) 192 | return 193 | } else { 194 | logs.Error("account server rpc status: %v, err : %v", res.Code, err) 195 | } 196 | } else { 197 | logs.Debug("rpc GetUserInfoByToken err : %v", err) 198 | } 199 | } else { 200 | logs.Debug("get rpc [%v:%v] client err : %v", common.GameConf.AccountHost, common.GameConf.AccountPort, err) 201 | } 202 | } 203 | } else { 204 | logs.Error("json unmarshal err : %v", err) 205 | } 206 | client.Room = nil 207 | case "catch_fish": 208 | if len(reqJson) < 2 { 209 | return 210 | } 211 | //42["catch_fish","{\"userId\":101,\"chairId\":1,\"bulletId\":\"1_324965\",\"fishId\":\"10318923\",\"sign\":\"8bfef2b82dc7b97e4ad386ec40b83d2b\"}"] 212 | catchFishReq := catchFishReq{} 213 | if err := json.Unmarshal([]byte(reqJson[1]), &catchFishReq); err == nil { 214 | bulletId := catchFishReq.BulletId 215 | client.catchFish(catchFishReq.FishId, bulletId) 216 | } else { 217 | logs.Error("catch_fish req err: %v", err) 218 | } 219 | case "ready": 220 | if len(reqJson) < 2 { 221 | return 222 | } 223 | reqData := make(map[string]int) 224 | if err := json.Unmarshal([]byte(reqJson[1]), &reqData); err == nil { 225 | userId := UserId(reqData["userId"]) 226 | client.Room.Users[userId].Ready = true 227 | if client.Room.Status == GameStatusWaitBegin { 228 | client.Room.Status = GameStatusFree 229 | //client.Room.begin() 230 | client.Room.Utils.BuildFishTrace() 231 | } 232 | client.UserInfo.Online = true 233 | roomUsers := make([]*UserInfo, 0) 234 | for i := 0; i < 4; i++ { 235 | seatHasPlayer := false 236 | for _, userInfo := range client.Room.Users { 237 | if userInfo.SeatIndex == i { 238 | userInfo.ConversionScore, err = strconv.ParseFloat(fmt.Sprintf("%.3f", float64(userInfo.Score)/1000), 64) 239 | if err != nil { 240 | logs.Error("ParseFloat [%v] err %v", userInfo.Score, err) 241 | } 242 | roomUsers = append(roomUsers, userInfo) 243 | seatHasPlayer = true 244 | } 245 | } 246 | if !seatHasPlayer { 247 | roomUsers = append(roomUsers, &UserInfo{ 248 | SeatIndex: i, 249 | }) 250 | } 251 | } 252 | client.sendToClient([]interface{}{ 253 | "game_sync_push", 254 | map[string]interface{}{ 255 | "roomBaseScore": client.Room.Conf.BaseScore, 256 | "seats": roomUsers, 257 | }, 258 | }) 259 | } else { 260 | logs.Error("user req ready json unmarshal err : %v", err) 261 | } 262 | case "user_fire": 263 | if len(reqJson) < 2 { 264 | return 265 | } 266 | bullet := Bullet{} 267 | if err := json.Unmarshal([]byte(reqJson[1]), &bullet); err == nil { 268 | client.Fire(&bullet) 269 | } else { 270 | // todo 没办法 客户端bulletId 传的int :( 271 | userFireLaserReq := &UserFireLaserReq{} 272 | if err := json.Unmarshal([]byte(reqJson[1]), &userFireLaserReq); err == nil { 273 | bullet.UserId = userFireLaserReq.UserId 274 | bullet.ChairId = userFireLaserReq.ChairId 275 | bullet.BulletKind = userFireLaserReq.BulletKind 276 | bullet.BulletId = "" 277 | bullet.Angle = userFireLaserReq.Angle 278 | bullet.Sign = userFireLaserReq.Sign 279 | bullet.LockFishId = userFireLaserReq.LockFishId 280 | client.UserInfo.Power = 0 281 | client.sendToOthers([]interface{}{ 282 | "user_fire_Reply", 283 | bullet, 284 | }) 285 | return 286 | } 287 | logs.Error("user fire json err: %v", err) 288 | } 289 | case "laser_catch_fish": 290 | if len(reqJson) < 2 { 291 | return 292 | } 293 | laserCatchReq := LaserCatchReq{} 294 | if err := json.Unmarshal([]byte(reqJson[1]), &laserCatchReq); err == nil { 295 | fishIdStrArr := strings.Split(laserCatchReq.Fishes, "-") 296 | if len(fishIdStrArr) == 0 { 297 | logs.Debug("user [%v] laser_catch_fish catch zero fish...") 298 | } 299 | killedFishes := make([]string, 0) 300 | addScore := 0 301 | for _, fishStr := range fishIdStrArr { 302 | if fishIdInt, err := strconv.Atoi(fishStr); err == nil { 303 | fishId := FishId(fishIdInt) 304 | if fish, ok := client.Room.AliveFish[fishId]; ok { 305 | killedFishes = append(killedFishes, strconv.Itoa(int(fish.FishId))) 306 | //加钱 307 | addScore += GetFishMulti(fish) * GetBulletMulti(BulletKind["bullet_kind_laser"]) * client.Room.Conf.BaseScore 308 | } else { 309 | logs.Debug("user [%v] laser_catch_fish fishId [%v] not in alive fish array...", client.UserInfo.UserId, fishId) 310 | } 311 | } else { 312 | logs.Error("laser_catch_fish err : fishId [%v] err", fishStr) 313 | } 314 | } 315 | //if addScore > client.Room.Conf.BaseScore*200 { //最大200倍 316 | // addScore = client.Room.Conf.BaseScore * 200 317 | //} 318 | client.UserInfo.Score += addScore 319 | client.UserInfo.Bill += addScore //记账 320 | catchFishAddScore, _ := strconv.ParseFloat(fmt.Sprintf("%.5f", float64(addScore)/1000), 64) 321 | client.Room.broadcast([]interface{}{ 322 | "catch_fish_reply", 323 | map[string]interface{}{ 324 | "userId": laserCatchReq.UserId, 325 | "chairId": laserCatchReq.ChairId, 326 | "fishId": strings.Join(killedFishes, ","), 327 | "addScore": catchFishAddScore, 328 | "isLaser": true, 329 | }, 330 | }) 331 | } else { 332 | logs.Error("laser_catch_fish err : %v", err) 333 | } 334 | case "user_lock_fish": 335 | if len(reqJson) < 2 { 336 | return 337 | } 338 | userLockFishReq := UserLockFishReq{} 339 | if err := json.Unmarshal([]byte(reqJson[1]), &userLockFishReq); err == nil { 340 | client.sendToOthers([]interface{}{ 341 | "lock_fish_reply", 342 | userLockFishReq, 343 | }) 344 | } 345 | case "user_frozen": 346 | if len(reqJson) < 2 { 347 | return 348 | } 349 | client.frozenScene(time.Now()) 350 | case "user_change_cannon": 351 | if len(reqJson) < 2 { 352 | return 353 | } 354 | userChangeCannonReq := make(map[string]int) 355 | if err := json.Unmarshal([]byte(reqJson[1]), &userChangeCannonReq); err == nil { 356 | if userChangeCannonReq["cannonKind"] < 1 { 357 | return 358 | } 359 | if userChangeCannonReq["cannonKind"] == BulletKind["bullet_kind_laser"] { 360 | if client.UserInfo.Power < 1 { 361 | return 362 | } 363 | } 364 | client.UserInfo.CannonKind = userChangeCannonReq["cannonKind"] 365 | client.sendToOthers([]interface{}{ 366 | "user_change_cannon_reply", 367 | userChangeCannonReq, 368 | }) 369 | } 370 | case "exit": 371 | client.sendToOthers([]interface{}{ 372 | "exit_notify_push", 373 | client.UserInfo.UserId, 374 | }) 375 | jsonByte, err := json.Marshal([]string{"exit_result"}) 376 | if err != nil { 377 | logs.Error("game ping json marshal err,%v", err) 378 | return 379 | } 380 | client.sendMsg(append([]byte{'4', '2'}, jsonByte...)) 381 | client.sendMsg([]byte{'4', '1'}) 382 | clientExit(client, false) 383 | 384 | case "dispress": 385 | case "disconnect": 386 | case "game_ping": 387 | jsonByte, err := json.Marshal([]string{"game_pong"}) 388 | if err != nil { 389 | logs.Error("game ping json marshal err,%v", err) 390 | return 391 | } 392 | client.sendMsg(append([]byte{'4', '2'}, jsonByte...)) 393 | case "client_exit": 394 | if client.UserInfo.Online { 395 | clientExit(client, true) 396 | } 397 | } 398 | } 399 | } 400 | 401 | func clientExit(client *Client, closeClient bool) { 402 | logs.Debug("user %v exit close client: %v ...", client.UserInfo.UserId, closeClient) 403 | if client.UserInfo.Bill != 0 { 404 | client.clearBill() 405 | } 406 | RoomMgr.RoomLock.Lock() 407 | logs.Info("clientExit get lock...") 408 | defer RoomMgr.RoomLock.Unlock() 409 | defer logs.Info("clientExit set free lock...") 410 | client.UserInfo.Online = false 411 | roomUserIdArr := make([]UserId, 0) 412 | if roomInfo, ok := RoomMgr.RoomsInfo[client.Room.RoomId]; ok { 413 | for _, roomUserId := range roomInfo.UserInfo { 414 | if roomUserId != client.UserInfo.UserId { 415 | roomUserIdArr = append(roomUserIdArr, roomUserId) 416 | } 417 | } 418 | roomInfo.UserInfo = roomUserIdArr 419 | delete(client.Room.Users, client.UserInfo.UserId) 420 | if closeClient { 421 | client.closeChan <- true 422 | close(client.closeChan) //关闭channel不影响取出关闭前传送的数据,继续取将得到零值 :-) 423 | } 424 | if len(client.Room.Users) == 0 { //房间无人,消除房间 425 | delete(RoomMgr.RoomsInfo, client.Room.RoomId) 426 | delete(RoomMgr.Rooms, client.Room.RoomId) 427 | logs.Debug("room %v is empty now ...", client.Room.RoomId) 428 | client.Room.Exit <- true 429 | logs.Debug("send exit sign succ ...") 430 | } 431 | //close(client.msgChan) 432 | } else { 433 | logs.Error("exit client not in room...") 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /game/service/room.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "encoding/json" 5 | "fish/common/tools" 6 | "fmt" 7 | "github.com/astaxie/beego/logs" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | type roomMgr struct { 13 | RoomLock sync.Mutex 14 | RoomsInfo map[RoomId]*RoomInfo //room暴露出去的信息和channel 15 | Rooms map[RoomId]*room 16 | RoomIdChan <-chan int64 17 | } 18 | 19 | type RoomInfo struct { 20 | UserInfo []UserId 21 | HttpReqChan chan *HttpReqData 22 | BaseScore int //差点忘了请求的房间类型要一致才能进入 23 | } 24 | 25 | var ( 26 | RoomMgr = &roomMgr{ 27 | RoomLock: sync.Mutex{}, 28 | RoomsInfo: make(map[RoomId]*RoomInfo), 29 | Rooms: make(map[RoomId]*room), //只在room的协程里操作 30 | RoomIdChan: make(<-chan int64), 31 | } 32 | ) 33 | 34 | const ( 35 | GameStatusWaitBegin = iota 36 | GameStatusFree 37 | GameStatusPlay 38 | GameStatusFormation 39 | GameStatusFrozen 40 | ) 41 | 42 | type RoomId int64 43 | 44 | type room struct { 45 | RoomId RoomId 46 | ActiveFish []*Fish //待激活的鱼 47 | CreateTime time.Time 48 | Users map[UserId]*UserInfo 49 | Conf *RoomConf 50 | FrozenEndTime time.Time 51 | //FormationEndTime time.Time 52 | Status int 53 | AliveFish map[FishId]*Fish 54 | AliveBullet map[BulletId]*Bullet 55 | Utils *FishUtil 56 | fishArrayEndTimer <-chan time.Time 57 | frozenEndTimer <-chan time.Time 58 | Exit chan bool 59 | ClientReqChan chan *clientReqData //todo 客户端的请求通过chan传递,省去加锁的写法.包括加入房间 60 | HttpReqChan chan *HttpReqData 61 | } 62 | 63 | type clientReqData struct { 64 | client *Client 65 | reqData []string 66 | } 67 | 68 | type HttpReqData struct { 69 | UserInfo UserInfo 70 | ErrChan chan error 71 | } 72 | 73 | type RoomConf struct { 74 | BaseScore int `json:"gamebasescore"` 75 | MinHaveScore int `json:"minhavescore"` 76 | MaxHaveScore int `json:"maxhavescore"` 77 | TaxRatio int `json:"-"` //抽水 千分之 78 | Creator UserId `json:"creator"` 79 | } 80 | 81 | func init() { 82 | if err := initGenerateUidTool(); err != nil { 83 | panic(err) 84 | } 85 | } 86 | 87 | func initGenerateUidTool() (err error) { 88 | if err, RoomMgr.RoomIdChan = tools.GenerateUid(1); err != nil { 89 | logs.Error("GenerateUid err: %v", err) 90 | return 91 | } 92 | return 93 | } 94 | 95 | func CreatePublicRoom(roomConf *RoomConf) (roomId RoomId) { 96 | //方法外获得锁 97 | roomId = RoomId(<-RoomMgr.RoomIdChan) 98 | RoomMgr.Rooms[roomId] = &room{ 99 | RoomId: roomId, 100 | ActiveFish: make([]*Fish, 0), 101 | CreateTime: time.Now(), 102 | Users: make(map[UserId]*UserInfo, 4), 103 | Conf: roomConf, 104 | FrozenEndTime: time.Time{}, 105 | //FormationEndTime: time.Time{}, 106 | Status: GameStatusWaitBegin, 107 | AliveFish: make(map[FishId]*Fish), 108 | AliveBullet: make(map[BulletId]*Bullet), 109 | Utils: &FishUtil{ 110 | //ActiveFish: make([]*Fish, 0), 111 | //Lock: sync.Mutex{}, 112 | BuildFishChan: make(chan *Fish, 10), 113 | StopBuildFish: make(chan bool), //暂停出鱼 114 | RestartBuildFish: make(chan bool), //重新开始出鱼 115 | Exit: make(chan bool, 1), //接收信号 116 | }, 117 | fishArrayEndTimer: make(<-chan time.Time), 118 | frozenEndTimer: make(<-chan time.Time), 119 | Exit: make(chan bool, 1), 120 | ClientReqChan: make(chan *clientReqData), 121 | HttpReqChan: make(chan *HttpReqData), 122 | } 123 | RoomMgr.RoomsInfo[roomId] = &RoomInfo{ 124 | UserInfo: make([]UserId, 0), 125 | //ClientReqChan: make(chan *clientReqData), 126 | HttpReqChan: RoomMgr.Rooms[roomId].HttpReqChan, 127 | BaseScore: roomConf.BaseScore, 128 | } 129 | 130 | RoomMgr.Rooms[roomId].begin() 131 | return 132 | } 133 | 134 | func (room *room) EnterRoom(userInfo *UserInfo) (err error) { 135 | logs.Debug("user %d request enter room %v", userInfo.UserId, room.RoomId) 136 | 137 | userCount := len(room.Users) 138 | if userCount >= 4 { 139 | logs.Error("enterRoom err: room [%v] is full", room.RoomId) 140 | return 141 | } 142 | seatIndex := -1 143 | out: 144 | for i := 0; i < 4; i++ { 145 | for _, roomUserInfo := range room.Users { 146 | if roomUserInfo.SeatIndex == i { 147 | continue out 148 | } 149 | } 150 | seatIndex = i 151 | break 152 | } 153 | 154 | if seatIndex == -1 { 155 | return fmt.Errorf("enterRoom roomId [%v] failed", room.RoomId) 156 | } 157 | userInfo.SeatIndex = seatIndex 158 | room.Users[userInfo.UserId] = userInfo 159 | return 160 | } 161 | 162 | func (room *room) begin() { 163 | logs.Debug("room %d begin", room.RoomId) 164 | buildNormalFishTicker := time.NewTicker(time.Second * 1) //普通鱼每秒刷一次 165 | buildGroupFishTicker := time.NewTicker(time.Second * 5 * 60) //鱼群 166 | flushTimeOutFishTicker := time.NewTicker(time.Second * 5) //清理过期鱼 167 | 168 | go func() { 169 | defer func() { 170 | logs.Trace("room %v exit...", room.RoomId) 171 | buildNormalFishTicker.Stop() 172 | buildGroupFishTicker.Stop() 173 | flushTimeOutFishTicker.Stop() 174 | room.Utils.Exit <- true 175 | go func() { //启动协程取数据,防止utils阻塞在出鱼阶段导致无法退出 :) 176 | for range room.Utils.BuildFishChan { 177 | } 178 | }() 179 | close(room.Exit) 180 | close(room.HttpReqChan) 181 | close(room.ClientReqChan) 182 | 183 | RoomMgr.RoomLock.Lock() 184 | logs.Info("exit room goroutine get lock...") 185 | defer RoomMgr.RoomLock.Unlock() 186 | defer logs.Info("exit room goroutine set free lock...") 187 | 188 | delete(RoomMgr.Rooms, room.RoomId) 189 | }() 190 | //defer room.Wg.Done() 191 | for { 192 | select { 193 | case <-buildNormalFishTicker.C: 194 | room.flushFish() 195 | case <-buildGroupFishTicker.C: 196 | if room.Status != GameStatusFree { 197 | continue 198 | } 199 | room.Utils.StopBuildFish <- true 200 | room.AliveFish = make(map[FishId]*Fish) //清理鱼 201 | room.buildFormation() 202 | case <-flushTimeOutFishTicker.C: 203 | now := time.Now() 204 | AliveFishCheck := make(map[FishId]*Fish) 205 | for _, fish := range room.AliveFish { 206 | if now.Sub(fish.ActiveTime) < 60*2*time.Second { 207 | AliveFishCheck[fish.FishId] = fish 208 | } 209 | } 210 | room.AliveFish = AliveFishCheck 211 | case fish := <-room.Utils.BuildFishChan: 212 | room.ActiveFish = append(room.ActiveFish, fish) 213 | case clientReq := <-room.ClientReqChan: 214 | //logs.Debug("room [%d] receive client message %v", room.RoomId, clientReq.reqData) 215 | handleUserRequest(clientReq) 216 | case httpReq := <-room.HttpReqChan: 217 | httpReq.ErrChan <- room.EnterRoom(&httpReq.UserInfo) 218 | close(httpReq.ErrChan) 219 | case <-room.Exit: 220 | return 221 | case <-room.fishArrayEndTimer: 222 | room.Status = GameStatusFree 223 | //room.AliveFish = make(map[FishId]*Fish) //清理鱼,因为时间有可能不同步,所以结束也不清理鱼 224 | room.Utils.RestartBuildFish <- true 225 | case <-room.frozenEndTimer: 226 | room.Status = GameStatusFree 227 | room.Utils.RestartBuildFish <- true 228 | } 229 | } 230 | }() 231 | } 232 | 233 | func (room *room) flushFish() { 234 | if room.Status != GameStatusFree { 235 | return 236 | } 237 | newFish := make([]*Fish, 0) 238 | for _, fish := range room.ActiveFish { 239 | if _, ok := room.AliveFish[fish.FishId]; ok { 240 | continue 241 | } 242 | //if len(room.AliveFish) < 30 { 243 | room.AliveFish[fish.FishId] = fish 244 | newFish = append(newFish, fish) 245 | //} else { 246 | // break 247 | //} 248 | } 249 | room.ActiveFish = make([]*Fish, 0) 250 | if len(newFish) > 0 { 251 | room.broadcast([]interface{}{"build_fish_reply", newFish}) 252 | } 253 | } 254 | 255 | func (room *room) buildFormation() { 256 | if room.Status != GameStatusFree { 257 | room.Utils.RestartBuildFish <- true 258 | return 259 | } 260 | room.Status = GameStatusFormation 261 | fishArrayData := BuildFishArray() 262 | activeTime := time.Now() 263 | for _, fishArray := range fishArrayData.FishArray { 264 | for _, arrayFish := range fishArray { 265 | room.AliveFish[arrayFish.FishId] = &Fish{ 266 | FishId: arrayFish.FishId, 267 | FishKind: arrayFish.FishKind, 268 | Speed: 0, 269 | ActiveTime: activeTime, 270 | } 271 | } 272 | } 273 | room.fishArrayEndTimer = time.After(fishArrayData.EndTime.Sub(time.Now())) 274 | room.broadcast([]interface{}{ 275 | "build_fishArray_reply", 276 | fishArrayData, 277 | }) 278 | } 279 | 280 | func (room *room) getBombFish() (killedFishes []*Fish) { 281 | for _, fish := range room.AliveFish { 282 | if len(killedFishes) == 20 { 283 | return 284 | } 285 | if fish.FishKind < FishKind11 { 286 | killedFishes = append(killedFishes, fish) 287 | } 288 | } 289 | return 290 | } 291 | 292 | //一网打尽 293 | func (room *room) getAllInOne(oneFish *Fish) (killedFishes []*Fish) { 294 | for _, fish := range room.AliveFish { 295 | if fish.FishKind >= FishKind23 && fish.FishKind <= FishKind26 { 296 | killedFishes = append(killedFishes, fish) 297 | } 298 | } 299 | return 300 | } 301 | 302 | //同类炸弹 303 | func (room *room) getSameFish(oneFish *Fish) (killedFishes []*Fish) { 304 | switch oneFish.FishKind { 305 | case FishKind31: 306 | for _, fish := range room.AliveFish { 307 | if fish.FishKind == FishKind31 || fish.FishKind == FishKind12 { 308 | killedFishes = append(killedFishes, fish) 309 | } 310 | } 311 | case FishKind32: 312 | for _, fish := range room.AliveFish { 313 | if fish.FishKind == FishKind32 || fish.FishKind == FishKind1 { 314 | killedFishes = append(killedFishes, fish) 315 | } 316 | } 317 | case FishKind33: 318 | for _, fish := range room.AliveFish { 319 | if fish.FishKind == FishKind33 || fish.FishKind == FishKind7 { 320 | killedFishes = append(killedFishes, fish) 321 | } 322 | } 323 | } 324 | return 325 | } 326 | 327 | func (room *room) broadcast(data []interface{}) { 328 | if dataByte, err := json.Marshal(data); err != nil { 329 | logs.Error("broadcast [%v] json marshal err :%v ", data, err) 330 | } else { 331 | dataByte = append([]byte{'4', '2'}, dataByte...) 332 | for _, userInfo := range room.Users { 333 | if userInfo.client != nil { 334 | userInfo.client.sendMsg(dataByte) 335 | } 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module fish 2 | 3 | go 1.12 4 | 5 | replace ( 6 | golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85 7 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a => github.com/golang/net v0.0.0-20181114220301-adae6a3d119a 8 | golang.org/x/sys v0.3.0 => github.com/golang/sys v0.3.0 9 | golang.org/x/text v0.3.0 => github.com/golang/text v0.3.0 10 | ) 11 | 12 | require ( 13 | git.apache.org/thrift.git v0.12.0 14 | github.com/apache/thrift v0.12.0 15 | github.com/astaxie/beego v1.11.1 16 | github.com/garyburd/redigo v1.6.0 17 | github.com/go-redis/redis v6.15.2+incompatible 18 | github.com/go-sql-driver/mysql v1.4.1 19 | github.com/golang/go v0.0.0-20190523013941-3e9d8e2e1bb9 // indirect 20 | github.com/gorilla/websocket v1.4.0 21 | github.com/jmoiron/sqlx v1.2.0 22 | github.com/orestonce/ChessGame v0.0.0-20190419000812-8e1a70c446b3 // indirect 23 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | git.apache.org/thrift.git v0.12.0 h1:CMxsZlAmxKs+VAZMlDDL0wXciMblJcutQbEe3A9CYUM= 2 | git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 3 | github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 4 | github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs= 5 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 6 | github.com/astaxie/beego v1.11.1 h1:6DESefxW5oMcRLFRKi53/6exzup/IR6N4EzzS1n6CnQ= 7 | github.com/astaxie/beego v1.11.1/go.mod h1:i69hVzgauOPSw5qeyF4GVZhn7Od0yG5bbCGzmhbWxgQ= 8 | github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ= 9 | github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU= 10 | github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY= 11 | github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= 12 | github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE= 13 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 14 | github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U= 15 | github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= 16 | github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= 17 | github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= 18 | github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 19 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 20 | github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= 21 | github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= 22 | github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 23 | github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4= 24 | github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 25 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 26 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= 27 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 28 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 29 | github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:uZvAcrsnNaCxlh1HorK5dUQHGmEKPh2H/Rl1kehswPo= 30 | github.com/golang/go v0.0.0-20190523013941-3e9d8e2e1bb9 h1:wdEw8NbuJZM04QMvzorGVoSx2yGH/OMxNEKan9uCSP0= 31 | github.com/golang/go v0.0.0-20190523013941-3e9d8e2e1bb9/go.mod h1:VnTjtYw+XLkxokOYpCb9NBW3cOTFO8+uqxF7o10XJQk= 32 | github.com/golang/net v0.0.0-20181114220301-adae6a3d119a h1:wxhJMi186V9aI731PnbZnsQ1aE0hEeDc/Gf9tTNIssU= 33 | github.com/golang/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:98y8FxUyMjTdJ5eOj/8vzuiVO14/dkJ98NYhEPG8QGY= 34 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 35 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 36 | github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= 37 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 38 | github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= 39 | github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= 40 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 41 | github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 42 | github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 43 | github.com/orestonce/ChessGame v0.0.0-20190419000812-8e1a70c446b3 h1:HYSeOb+ue/GSUi+8Tuc0mliUpBG4LgdEACw6ZZdwq0o= 44 | github.com/orestonce/ChessGame v0.0.0-20190419000812-8e1a70c446b3/go.mod h1:DzMpQbwxDX21pjkuZGqrl4AlcKPZIxUFPQ1qHiWYI+M= 45 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 46 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 47 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 48 | github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= 49 | github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= 50 | github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= 51 | github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= 52 | github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= 53 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 54 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 55 | -------------------------------------------------------------------------------- /hall/common/config.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | var ( 4 | HallConf = &HallServiceConf{} 5 | ) 6 | 7 | type HallServiceConf struct { 8 | AccountHost string 9 | AccountPort int 10 | HallHost string 11 | HallPort int 12 | HallSecret string 13 | LogPath string 14 | LogLevel string 15 | Version string 16 | 17 | AppId int //qq登录 18 | AppKey string 19 | RedirectUri string 20 | } 21 | -------------------------------------------------------------------------------- /hall/controllers/enter_public_room.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "github.com/astaxie/beego/logs" 7 | "net/http" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | func EnterPublicRoom(w http.ResponseWriter, r *http.Request) { 13 | lock.Lock() 14 | defer lock.Unlock() 15 | minLoad := 0 16 | gameUrl := "" 17 | for serverUrl,load := range serverInfo{ 18 | if minLoad == 0 { 19 | minLoad = load 20 | gameUrl = serverUrl 21 | }else{ 22 | if load <= minLoad { 23 | minLoad = load 24 | gameUrl = serverUrl 25 | } 26 | } 27 | } 28 | if gameUrl == "" { 29 | logs.Error("no game server running ...") 30 | return 31 | } 32 | target := "http://" + gameUrl + r.URL.Path 33 | //target := "http://127.0.0.1:9001" + r.URL.Path 34 | timeStamp := time.Now().Unix() 35 | data := []byte("t" + strconv.Itoa(int(timeStamp))) 36 | token := fmt.Sprintf("%x", md5.Sum(data)) 37 | if len(r.URL.RawQuery) > 0 { 38 | target += "?" + r.URL.RawQuery + "&token=" + token + "&t=" + strconv.Itoa(int(timeStamp)) 39 | } else { 40 | target += "?token=" + token + "&t=" + strconv.Itoa(int(timeStamp)) 41 | } 42 | w.Header().Set("Access-Control-Allow-Origin", "*") 43 | http.Redirect(w, r, target, http.StatusMovedPermanently) 44 | } 45 | -------------------------------------------------------------------------------- /hall/controllers/get_message.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/tools" 7 | "fish/hall/common" 8 | "github.com/astaxie/beego/logs" 9 | "net/http" 10 | "strconv" 11 | ) 12 | 13 | func GetMessage(w http.ResponseWriter, r *http.Request) { 14 | defer func() { 15 | if r := recover(); r != nil { 16 | logs.Error("GetUserInfo panic:%v ", r) 17 | } 18 | }() 19 | messageType := r.FormValue("type") 20 | if len(messageType) == 0 { 21 | return 22 | } 23 | //logs.Debug("new request url:[%s]",r.URL) 24 | ret := map[string]interface{}{ 25 | "errcode": 1, 26 | "errmsg": "get message failed", 27 | } 28 | //logs.Debug("get rpc client %v:%v", common.HallConf.AccountHost, common.HallConf.AccountPort) 29 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 30 | defer func() { 31 | if err := closeTransportHandler(); err != nil { 32 | logs.Error("close rpc err: %v", err) 33 | } 34 | }() 35 | if res, err := client.GetMessage(context.Background(), messageType); err == nil { 36 | ret = map[string]interface{}{ 37 | "errcode": 0, 38 | "errmsg": "ok", 39 | "msg": res, 40 | "version": common.HallConf.Version, 41 | } 42 | } else { 43 | logs.Error("call rpc GetMessage err: %v", err) 44 | } 45 | } 46 | defer func() { 47 | data, err := json.Marshal(ret) 48 | if err != nil { 49 | logs.Error("json marsha1 failed err:%v", err) 50 | return 51 | } 52 | w.Header().Set("Access-Control-Allow-Origin", "*") 53 | if _, err := w.Write(data); err != nil { 54 | logs.Error("CreateRoom err: %v", err) 55 | } 56 | }() 57 | } 58 | -------------------------------------------------------------------------------- /hall/controllers/get_server_info.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "encoding/json" 5 | "fish/hall/common" 6 | "github.com/astaxie/beego/logs" 7 | "net/http" 8 | "strconv" 9 | ) 10 | 11 | func GetServerInfo(w http.ResponseWriter, r *http.Request) { 12 | defer func() { 13 | if r := recover(); r != nil { 14 | logs.Error("GetUserInfo panic:%v ", r) 15 | } 16 | }() 17 | //logs.Debug("new request url:[%s]",r.URL) 18 | ret := map[string]interface{}{ 19 | "appweb": "please wait", 20 | "hall": common.HallConf.HallHost + ":" + strconv.Itoa(common.HallConf.HallPort), 21 | "version": common.HallConf.Version, 22 | } 23 | defer func() { 24 | data, err := json.Marshal(ret) 25 | if err != nil { 26 | logs.Error("json marsha1 failed err:%v", err) 27 | return 28 | } 29 | w.Header().Set("Access-Control-Allow-Origin", "*") 30 | if _, err := w.Write(data); err != nil { 31 | logs.Error("CreateRoom err: %v", err) 32 | } 33 | }() 34 | } 35 | -------------------------------------------------------------------------------- /hall/controllers/get_user_status.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fish/hall/common" 9 | "github.com/astaxie/beego/logs" 10 | "net/http" 11 | "strconv" 12 | ) 13 | 14 | func GetUserStatus(w http.ResponseWriter, r *http.Request) { 15 | defer func() { 16 | if r := recover(); r != nil { 17 | logs.Error("GetUserInfo panic:%v ", r) 18 | } 19 | }() 20 | //logs.Debug("new request url:[%s]",r.URL) 21 | account := r.FormValue("account") 22 | if len(account) == 0 { 23 | return 24 | } 25 | token := r.FormValue("sign") 26 | if len(token) == 0 { 27 | return 28 | } 29 | ret := map[string]interface{}{ 30 | "errcode": 1, 31 | "errmsg": "failed", 32 | } 33 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 34 | defer func() { 35 | if err := closeTransportHandler(); err != nil { 36 | logs.Error("close rpc err: %v", err) 37 | } 38 | }() 39 | if res, err := client.GetUserInfoByToken(context.Background(), token); err == nil { 40 | if res.Code == rpc.ErrorCode_Success { 41 | ret = map[string]interface{}{ 42 | "errcode": 0, 43 | "errmsg": "ok", 44 | "gems": res.UserObj.Gems, 45 | } 46 | } 47 | } else { 48 | logs.Error("call rpc GetUserStatus err: %v", err) 49 | } 50 | } 51 | defer func() { 52 | data, err := json.Marshal(ret) 53 | if err != nil { 54 | logs.Error("json marsha1 failed err:%v", err) 55 | return 56 | } 57 | w.Header().Set("Access-Control-Allow-Origin", "*") 58 | if _, err := w.Write(data); err != nil { 59 | logs.Error("CreateRoom err: %v", err) 60 | } 61 | }() 62 | } 63 | -------------------------------------------------------------------------------- /hall/controllers/guest.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fish/hall/common" 9 | "fmt" 10 | "github.com/astaxie/beego/logs" 11 | "math/rand" 12 | "net/http" 13 | "strconv" 14 | "time" 15 | ) 16 | 17 | func Guest(w http.ResponseWriter, r *http.Request) { 18 | //defer func() { 19 | // if r := recover(); r != nil { 20 | // logs.Error("Guest panic:%v ", r) 21 | // } 22 | //}() 23 | 24 | sign := r.FormValue("sign") 25 | if len(sign) == 0 || sign == "null" { 26 | qqLoginUrl := fmt.Sprintf("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%d&redirect_uri=%s&state=1", appId, redirectUri) 27 | ret := map[string]interface{}{ 28 | "errcode": 1, 29 | "qqLoginUrl": qqLoginUrl, 30 | } 31 | w.Header().Set("Access-Control-Allow-Origin", "*") 32 | data, err := json.Marshal(ret) 33 | if err != nil { 34 | logs.Error("json marsha1 failed err:%v", err) 35 | return 36 | } 37 | w.Header().Set("Access-Control-Allow-Origin", "*") 38 | if _, err := w.Write(data); err != nil { 39 | logs.Error("CreateRoom err: %v", err) 40 | } 41 | } else { 42 | rand.Seed(time.Now().UnixNano()) 43 | //account = firstName[rand.Intn(len(firstName)-1)] + secondName[rand.Intn(len(secondName)-1)] 44 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 45 | defer func() { 46 | if err := closeTransportHandler(); err != nil { 47 | logs.Error("close rpc err: %v", err) 48 | } 49 | }() 50 | if r, err := client.GetUserInfoByToken(context.Background(), sign); err == nil { 51 | if r.Code == rpc.ErrorCode_Success { 52 | sign = r.UserObj.Token 53 | } 54 | ret := map[string]interface{}{ 55 | "errcode": 0, 56 | "errmsg": "ok", 57 | //"account": "guest_" + account, 58 | "account": r.UserObj.NickName, 59 | "halladdr": common.HallConf.HallHost + ":" + strconv.Itoa(common.HallConf.HallPort), 60 | "sign": sign, 61 | } 62 | defer func() { 63 | data, err := json.Marshal(ret) 64 | if err != nil { 65 | logs.Error("json marsha1 failed err:%v", err) 66 | return 67 | } 68 | w.Header().Set("Access-Control-Allow-Origin", "*") 69 | if _, err := w.Write(data); err != nil { 70 | logs.Error("CreateRoom err: %v", err) 71 | } 72 | }() 73 | } else { 74 | logs.Error("call rpc Guest err: %v", err) 75 | } 76 | 77 | } else { 78 | logs.Error("get rpc client err: %v", err) 79 | } 80 | } 81 | 82 | /*defer func() { 83 | if r := recover(); r != nil { 84 | logs.Error("Guest panic:%v ", r) 85 | } 86 | }() 87 | //logs.Debug("new request url:[%s]",r.URL) 88 | account := r.FormValue("account") 89 | if len(account) == 0 { 90 | return 91 | } 92 | rand.Seed(time.Now().UnixNano()) 93 | account = firstName[rand.Intn(len(firstName)-1)] + secondName[rand.Intn(len(secondName)-1)] 94 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 95 | defer func() { 96 | if err := closeTransportHandler(); err != nil { 97 | logs.Error("close rpc err: %v", err) 98 | } 99 | }() 100 | sign := "" 101 | if r, err := client.CreateNewUser(context.Background(), account, "1", 1000000); err == nil { 102 | if r.Code == rpc.ErrorCode_Success { 103 | sign = r.UserObj.Token 104 | } 105 | } else { 106 | logs.Error("call rpc Guest err: %v", err) 107 | } 108 | ret := map[string]interface{}{ 109 | "errcode": 0, 110 | "errmsg": "ok", 111 | //"account": "guest_" + account, 112 | "account": account, 113 | "halladdr": common.HallConf.HallHost + ":" + strconv.Itoa(common.HallConf.HallPort), 114 | "sign": sign, 115 | } 116 | defer func() { 117 | data, err := json.Marshal(ret) 118 | if err != nil { 119 | logs.Error("json marsha1 failed err:%v", err) 120 | return 121 | } 122 | w.Header().Set("Access-Control-Allow-Origin", "*") 123 | if _, err := w.Write(data); err != nil { 124 | logs.Error("CreateRoom err: %v", err) 125 | } 126 | }() 127 | } else { 128 | logs.Error("get rpc client err: %v", err) 129 | }*/ 130 | } 131 | 132 | var ( 133 | firstName = []string{ 134 | "赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", 135 | "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", 136 | "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", 137 | "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", 138 | "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", 139 | "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", 140 | "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", 141 | "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", 142 | "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", 143 | "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", 144 | } 145 | secondName = []string{ 146 | "子璇", "淼", "国栋", "夫子", "瑞堂", "甜", "敏", "尚", "国贤", "贺祥", "晨涛", 147 | "昊轩", "易轩", "益辰", "益帆", "益冉", "瑾春", "瑾昆", "春齐", "杨", "文昊", 148 | "东东", "雄霖", "浩晨", "熙涵", "溶溶", "冰枫", "欣欣", "宜豪", "欣慧", "建政", 149 | "美欣", "淑慧", "文轩", "文杰", "欣源", "忠林", "榕润", "欣汝", "慧嘉", "新建", 150 | "建林", "亦菲", "林", "冰洁", "佳欣", "涵涵", "禹辰", "淳美", "泽惠", "伟洋", 151 | "涵越", "润丽", "翔", "淑华", "晶莹", "凌晶", "苒溪", "雨涵", "嘉怡", "佳毅", 152 | "子辰", "佳琪", "紫轩", "瑞辰", "昕蕊", "萌", "明远", "欣宜", "泽远", "欣怡", 153 | "佳怡", "佳惠", "晨茜", "晨璐", "运昊", "汝鑫", "淑君", "晶滢", "润莎", "榕汕", 154 | "佳钰", "佳玉", "晓庆", "一鸣", "语晨", "添池", "添昊", "雨泽", "雅晗", "雅涵", 155 | "清妍", "诗悦", "嘉乐", "晨涵", "天赫", "玥傲", "佳昊", "天昊", "萌萌", "若萌", 156 | } 157 | ) 158 | -------------------------------------------------------------------------------- /hall/controllers/login.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fish/hall/common" 9 | "github.com/astaxie/beego/logs" 10 | "net/http" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | func Login(w http.ResponseWriter, r *http.Request) { 16 | defer func() { 17 | if r := recover(); r != nil { 18 | logs.Error("GetUserInfo panic:%v ", r) 19 | } 20 | }() 21 | //logs.Debug("new request url:[%s]",r.URL) 22 | account := r.FormValue("account") 23 | if len(account) == 0 { 24 | return 25 | } 26 | token := r.FormValue("sign") 27 | if len(token) == 0 { 28 | return 29 | } 30 | ret := map[string]interface{}{ 31 | "errcode": 0, 32 | "errmsg": "ok", 33 | } 34 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 35 | defer func() { 36 | if err := closeTransportHandler(); err != nil { 37 | logs.Error("close rpc err: %v", err) 38 | } 39 | }() 40 | ip := strings.TrimSpace(strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]) 41 | if res, err := client.GetUserInfoByToken(context.Background(), token); err == nil { 42 | if res.Code == rpc.ErrorCode_Success { 43 | ret = map[string]interface{}{ 44 | "errcode": 0, 45 | "errmsg": "ok", 46 | "account": res.UserObj.NickName, 47 | "userid": res.UserObj.UserId, 48 | "name": res.UserObj.NickName, 49 | "headimg": res.UserObj.HeadImg, 50 | "lv": res.UserObj.Lv, 51 | "exp": res.UserObj.Exp, 52 | "coins": res.UserObj.Gems, 53 | "vip": res.UserObj.Vip, 54 | "money": res.UserObj.Gems, 55 | "gems": res.UserObj.Gems, 56 | "ip": ip, 57 | "sex": res.UserObj.Sex, 58 | "RenameCount": res.UserObj.ReNameCount, 59 | "ReHeadCount": res.UserObj.ReHeadCount, 60 | "item": map[string]int64{ 61 | "ice": res.UserObj.Ice, 62 | }, 63 | } 64 | } 65 | } else { 66 | logs.Error("call rpc Login err: %v", err) 67 | } 68 | } 69 | defer func() { 70 | data, err := json.Marshal(ret) 71 | if err != nil { 72 | logs.Error("json marsha1 failed err:%v", err) 73 | return 74 | } 75 | w.Header().Set("Access-Control-Allow-Origin", "*") 76 | if _, err := w.Write(data); err != nil { 77 | logs.Error("CreateRoom err: %v", err) 78 | } 79 | }() 80 | } 81 | -------------------------------------------------------------------------------- /hall/controllers/qq_callback.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fish/common/api/thrift/gen-go/rpc" 7 | "fish/common/tools" 8 | "fish/hall/common" 9 | "fmt" 10 | "github.com/astaxie/beego/logs" 11 | "io/ioutil" 12 | "math/rand" 13 | "net/http" 14 | "net/url" 15 | "strconv" 16 | "strings" 17 | ) 18 | 19 | type qqUserInfo struct { 20 | Ret int `json:"ret"` 21 | Msg string `json:"msg"` 22 | IsLost int `json:"is_lost"` 23 | Nickname string `json:"nickname"` 24 | Gender string `json:"gender"` 25 | Province string `json:"province"` 26 | City string `json:"city"` 27 | Year string `json:"year"` 28 | Constellation string `json:"constellation"` 29 | FigureUrl string `json:"figureurl"` 30 | FigureUrl1 string `json:"figureurl_1"` 31 | FigureUrl2 string `json:"figureurl_2"` 32 | FigureUrlQQ1 string `json:"figureurl_qq_1"` 33 | FigureUrlQQ2 string `json:"figureurl_qq_2"` 34 | FigureUrlQQ string `json:"figureurl_qq"` 35 | FigureUrlType string `json:"figureurl_type"` 36 | IsYellowVip string `json:"is_yellow_vip"` 37 | Vip string `json:"vip"` 38 | YellowVipLevel string `json:"yellow_vip_level"` 39 | Level string `json:"level"` 40 | IsYellowYearVip string `json:"is_yellow_year_vip"` 41 | } 42 | 43 | func QQCallback(w http.ResponseWriter, r *http.Request) { 44 | defer func() { 45 | if r := recover(); r != nil { 46 | logs.Error("QQCallback panic:%v ", r) 47 | } 48 | }() 49 | var sign string 50 | defer func() { 51 | var script = fmt.Sprintf(``,sign) 52 | w.Header().Set("Access-Control-Allow-Origin", "*") 53 | if _, err := w.Write([]byte(script)); err != nil { 54 | logs.Error("QQCallback err: %v", err) 55 | } 56 | }() 57 | r.ParseForm() 58 | code := r.FormValue("code") 59 | if len(code) == 0 { 60 | logs.Error("QQCallback :code is null") 61 | } 62 | getTokenUrl := fmt.Sprintf("https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%d&client_secret=%s&code=%s&redirect_uri=%s", appId, AppKey, code, redirectUri) 63 | resp, err := http.Get(getTokenUrl) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | defer resp.Body.Close() 69 | body, err := ioutil.ReadAll(resp.Body) 70 | if err != nil { 71 | panic(err) 72 | } 73 | m, _ := url.ParseQuery(string(body)) 74 | if len(m["access_token"]) > 0 { 75 | accessToken := m["access_token"][0] 76 | 77 | getOpenIdUrl := fmt.Sprintf("https://graph.qq.com/oauth2.0/me?access_token=%s", accessToken) 78 | resp, err := http.Get(getOpenIdUrl) 79 | if err != nil { 80 | panic(err) 81 | } 82 | 83 | defer resp.Body.Close() 84 | body, err := ioutil.ReadAll(resp.Body) 85 | if err != nil { 86 | panic(err) 87 | } 88 | callback := string(body) 89 | //callback( {"client_id":"101673379","openid":"EC0F4A930140B2581EDC71A08D824985"} ); 90 | start := strings.Index(callback, "{") 91 | end := strings.Index(callback, "}") 92 | retMapStr := callback[start : end+1] 93 | retMap := make(map[string]string) 94 | if err := json.Unmarshal([]byte(retMapStr), &retMap); err != nil { 95 | panic(err) 96 | } 97 | if len(retMap["openid"]) != 0 { 98 | getUserInfoUrl := fmt.Sprintf("https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%d&openid=%s", accessToken, appId, retMap["openid"]) 99 | resp, err := http.Get(getUserInfoUrl) 100 | if err != nil { 101 | panic(err) 102 | } 103 | 104 | defer resp.Body.Close() 105 | body, err = ioutil.ReadAll(resp.Body) 106 | if err != nil { 107 | panic(err) 108 | } 109 | 110 | qqUserInfo := &qqUserInfo{} 111 | if err := json.Unmarshal([]byte(body), qqUserInfo); err != nil { 112 | panic(err) 113 | } 114 | 115 | fmt.Println(string(body)) 116 | fmt.Println(qqUserInfo) 117 | fmt.Println(qqUserInfo.Nickname) 118 | if client, closeTransportHandler, err := tools.GetRpcClient(common.HallConf.AccountHost, strconv.Itoa(common.HallConf.AccountPort)); err == nil { 119 | defer func() { 120 | if err := closeTransportHandler(); err != nil { 121 | logs.Error("close rpc err: %v", err) 122 | } 123 | }() 124 | var sex int8 125 | if qqUserInfo.Gender != "男" { 126 | sex = 1 127 | } 128 | if resp, err := client.CreateQQUser(context.Background(), &rpc.UserInfo{ 129 | UserName: qqUserInfo.Nickname, 130 | NickName: qqUserInfo.Nickname, 131 | Sex: sex, 132 | HeadImg: "1", 133 | Lv: int32(rand.Intn(7)), 134 | Exp: 0, 135 | Vip: int8(rand.Intn(7)), 136 | Gems: 10000, 137 | Ice: 10, 138 | QqInfo: &rpc.QqInfo{ 139 | OpenId: retMap["openid"], 140 | FigureUrl: qqUserInfo.FigureUrlQQ1, 141 | Province: qqUserInfo.Province, 142 | City: qqUserInfo.City, 143 | TotalSpending: 0, 144 | }, 145 | }); err == nil { 146 | sign = resp.UserObj.Token 147 | } 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /hall/controllers/qq_login.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "fmt" 5 | "github.com/astaxie/beego/logs" 6 | "net/http" 7 | ) 8 | var ( 9 | appId = 101673379 10 | AppKey = "c18b1b56f2f88ef423bfeadbad9a816c" 11 | redirectUri = "http://fish.blzz.shop/qq/message" 12 | ) 13 | func QQLogin(w http.ResponseWriter, r *http.Request) { 14 | defer func() { 15 | if r := recover(); r != nil { 16 | logs.Error("QQLogin panic:%v ", r) 17 | } 18 | }() 19 | 20 | w.Header().Set("Access-Control-Allow-Origin", "*") 21 | //qqLoginUrl := fmt.Sprintf("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%d&redirect_uri=%s&state=%d&display=%s",appId,redirectUri,1,"mobile") 22 | qqLoginUrl := fmt.Sprintf("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%d&redirect_uri=%s&state=%d&display=%s",appId,redirectUri,1,"pc") 23 | http.Redirect(w,r,qqLoginUrl,302) 24 | } 25 | -------------------------------------------------------------------------------- /hall/controllers/register_game_server.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "crypto/md5" 5 | "fish/hall/common" 6 | "fmt" 7 | "github.com/astaxie/beego/logs" 8 | "net/http" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | var lock = sync.Mutex{} 14 | var serverInfo = make(map[string]int) 15 | //todo 优化的空间:可以加个注销服务接口。心跳加入时间,长时间未发送心跳的游戏服务器由大厅发起询问或暂时挂起。懒得做 :( 16 | 17 | func RegisterGameServer(w http.ResponseWriter, r *http.Request) { 18 | ret := "failed" 19 | defer func() { 20 | w.Header().Set("Access-Control-Allow-Origin", "*") 21 | if _, err := w.Write([]byte(ret)); err != nil { 22 | logs.Error("CreateRoom err: %v", err) 23 | } 24 | }() 25 | gameHost := r.FormValue("gameHost") 26 | if len(gameHost) == 0 { 27 | logs.Error("load game server failed,err : invalid param gameHost %v", gameHost) 28 | return 29 | } 30 | gamePort := r.FormValue("gamePort") 31 | if len(gamePort) == 0 { 32 | logs.Error("load game server failed,err : invalid param gamePort %v", gamePort) 33 | return 34 | } 35 | loadStr := r.FormValue("load") 36 | if len(loadStr) == 0 { 37 | logs.Error("load game server failed,err : invalid param load %v ", loadStr) 38 | return 39 | } 40 | t := r.FormValue("t") 41 | if len(t) == 0 { 42 | logs.Error("load game server failed,err : invalid param t %v ", t) 43 | return 44 | } 45 | sign := r.FormValue("sign") 46 | if len(sign) == 0 { 47 | logs.Error("load game server failed,err : invalid param sign %v ", sign) 48 | return 49 | } 50 | if fmt.Sprintf("%x", md5.Sum([]byte(common.HallConf.HallSecret+t))) != sign { 51 | logs.Error("load game server failed,check sign failed ") 52 | return 53 | } 54 | if loadInt, err := strconv.Atoi(loadStr); err != nil { 55 | logs.Error("load game server [%v:%v],err : invalid param load [%v]", gameHost, gamePort, loadStr) 56 | return 57 | } else { 58 | ret = "success" 59 | serverUrl := gameHost + ":" + gamePort 60 | lock.Lock() 61 | defer lock.Unlock() 62 | serverInfo[serverUrl] = loadInt 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /hall/main/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fish/hall/common" 5 | "fmt" 6 | "github.com/astaxie/beego/config" 7 | ) 8 | 9 | func initConf() (err error) { 10 | conf, err := config.NewConfig("ini", "./common/conf/hall.conf") 11 | if err != nil { 12 | fmt.Println("new hall config failed,err:", err) 13 | return 14 | } 15 | 16 | common.HallConf.HallHost = conf.String("hall_host") 17 | if common.HallConf.HallHost == "" { 18 | return fmt.Errorf("conf err: hall_host is null") 19 | } 20 | 21 | common.HallConf.HallPort, err = conf.Int("hall_port") 22 | if err != nil { 23 | return fmt.Errorf("conf err: hall_port is null") 24 | } 25 | 26 | common.HallConf.HallSecret = conf.String("hall_secret") 27 | if common.HallConf.HallSecret == "" { 28 | return fmt.Errorf("conf err: hall_secret is null") 29 | } 30 | 31 | common.HallConf.LogPath = conf.String("log_path") 32 | if common.HallConf.LogPath == "" { 33 | return fmt.Errorf("conf err: log_path is null") 34 | } 35 | 36 | common.HallConf.LogLevel = conf.String("log_level") 37 | if common.HallConf.LogLevel == "" { 38 | return fmt.Errorf("conf err: log_level is null") 39 | } 40 | 41 | common.HallConf.Version = conf.String("version") 42 | if common.HallConf.Version == "" { 43 | return fmt.Errorf("conf err: version is null") 44 | } 45 | 46 | accountConf, err := config.NewConfig("ini", "./common/conf/account.conf") 47 | if err != nil { 48 | fmt.Println("new account config failed,err:", err) 49 | return 50 | } 51 | common.HallConf.AccountHost = accountConf.String("account_host") 52 | if common.HallConf.AccountHost == "" { 53 | return fmt.Errorf("conf err: account_host is null") 54 | } 55 | 56 | common.HallConf.AccountPort, err = accountConf.Int("account_port") 57 | if err != nil { 58 | return fmt.Errorf("conf err: account_port is null") 59 | } 60 | 61 | common.HallConf.AppId, err = conf.Int("app_id") 62 | if err != nil { 63 | return fmt.Errorf("conf err: app_id is null") 64 | } 65 | common.HallConf.AppKey = conf.String("app_key") 66 | if common.HallConf.AppKey == "" { 67 | return fmt.Errorf("conf err: app_key is null") 68 | } 69 | common.HallConf.RedirectUri = conf.String("redirect_uri") 70 | if common.HallConf.RedirectUri == "" { 71 | return fmt.Errorf("conf err: redirect_uri is null") 72 | } 73 | 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /hall/main/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fish/hall/common" 6 | "github.com/astaxie/beego/logs" 7 | ) 8 | 9 | func conversionLogLevel(logLevel string) int { 10 | switch logLevel { 11 | case "debug": 12 | return logs.LevelDebug 13 | case "warn": 14 | return logs.LevelWarn 15 | case "info": 16 | return logs.LevelInfo 17 | case "trace": 18 | return logs.LevelTrace 19 | } 20 | return logs.LevelDebug 21 | } 22 | 23 | func initLogger() (err error) { 24 | config := make(map[string]interface{}) 25 | config["filename"] = common.HallConf.LogPath 26 | config["level"] = conversionLogLevel(common.HallConf.LogLevel) 27 | 28 | configStr, err := json.Marshal(config) 29 | if err != nil { 30 | return 31 | } 32 | err = logs.SetLogger(logs.AdapterFile, string(configStr)) 33 | return 34 | } 35 | 36 | func initSec() (err error) { 37 | err = initLogger() 38 | if err != nil { 39 | return 40 | } 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /hall/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fish/hall/common" 5 | _ "fish/hall/router" 6 | "flag" 7 | "fmt" 8 | "github.com/astaxie/beego/logs" 9 | "net/http" 10 | ) 11 | 12 | func main() { 13 | err := initConf() 14 | if err != nil { 15 | logs.Error("init conf err: %v", err) 16 | return 17 | } 18 | 19 | err = initSec() 20 | if err != nil { 21 | logs.Error("init sec err: %v", err) 22 | return 23 | } 24 | 25 | var addr = flag.String("addr", fmt.Sprintf(":%d", common.HallConf.HallPort), "http service address") 26 | logs.Debug("hall server listen port %v",*addr) 27 | err = http.ListenAndServe(*addr, nil) 28 | if err != nil { 29 | logs.Error("ListenAndServe err: %v", err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /hall/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fish/hall/controllers" 5 | "net/http" 6 | ) 7 | 8 | func init() { 9 | http.HandleFunc("/get_serverinfo", controllers.GetServerInfo) 10 | http.HandleFunc("/guest", controllers.Guest) 11 | http.HandleFunc("/login", controllers.Login) 12 | http.HandleFunc("/get_user_status", controllers.GetUserStatus) 13 | http.HandleFunc("/get_message", controllers.GetMessage) 14 | http.HandleFunc("/enter_public_room", controllers.EnterPublicRoom) 15 | http.HandleFunc("/register_game_server", controllers.RegisterGameServer) 16 | 17 | http.HandleFunc("/qq/login", controllers.QQLogin) 18 | http.HandleFunc("/qq/message", controllers.QQCallback) 19 | } 20 | -------------------------------------------------------------------------------- /start_account.bat: -------------------------------------------------------------------------------- 1 | call account.exe 2 | pause -------------------------------------------------------------------------------- /start_fish.bat: -------------------------------------------------------------------------------- 1 | call fish.exe 2 | pause -------------------------------------------------------------------------------- /start_hall.bat: -------------------------------------------------------------------------------- 1 | call hall.exe 2 | pause -------------------------------------------------------------------------------- /wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwg255/fish/a91a14e1f1f100d249b93bbdbb1696baafb708fd/wx.jpg -------------------------------------------------------------------------------- /z-start_all_server.bat: -------------------------------------------------------------------------------- 1 | start %~dp0\start_account.bat 2 | start %~dp0\start_hall.bat 3 | start %~dp0\start_fish.bat --------------------------------------------------------------------------------